10 2 3 1 以函式方式使用陣列

2021-06-27 23:48:18 字數 3094 閱讀 7639

10.2.3.1 以函式方式使用陣列 

我們先來看乙個 f# 的例子,這是兩個f# 庫處理陣列的重要的高階函式,然後,用 c# 實現相同的功能。清單 10.12 的中指令碼,先用隨機數初始化乙個陣列,然後,計算出它們的平方。

清單 10.12 處理陣列的函式式方法(f# interactive)

> let rnd = new system.random();; 

val rnd : system.random

> let numbers = array.init 5 (fun _-> rnd.next(10));;   [1]

val numbers : int array = [|1; 0; 7; 2; 2|]

> let squares = numbers |> array.map(fun n -> (n, n*n));;   [2]

val squares : (int * int) array = [| ... |]

> for sq in squares do  | 從結果陣列中輸出元組

printf "%a " sq;;      |

(1, 1) (0, 0) (7, 49) (2, 4) (2, 4)

我們使用的第乙個高階函式是 array.init [1],類似於在清單 10.2 中討論過的 list.int,它使用給定的函式初始化陣列;第二個函式是 array.map [2],與我們熟悉的 list.map 函式功能相同,在這個例子中,我們用它來建立乙個元組陣列,結果的每個元素包含原始的整數及其平方。

關鍵在於,這個例子中,我們在整個**中都沒有使用賦值運算子。第乙個操作構造新的陣列,第二個操作沒有修改這個陣列,而是返回另乙個新建立的陣列。雖然,陣列是可變的,但是,我們在**中,使用高階函式來處理,根本不改變它們。如果我們使用函式式列表,這個例子一樣能執行。

在陣列和列表之間選擇

我們已經看到,陣列和列表的使用方式是相似的,因此,就有乙個什麼時候選擇哪乙個的問題。第一點要考慮的是型別是否可變。函式程式設計極力強調資料型別是不可變的,我們將在下一章和第十四章看到的實際例子,說明為什麼這是值得的。我們能夠以函式方式處理陣列,但在保證程式的正確性方面,列表要更強一些。

另一點要考慮的是,對於某些操作,一種資料型別比另外的資料型別,更容易或更有效。在列表的前面追加元素,是比複製陣列的內容到稍大一點的新陣列中,要更容易。另一方面,對於隨機訪問,陣列更好。處理陣列的操作往往更快。我們可以看乙個簡單的使用 #time 指令的例子:

let l = [ 1 .. 100000 ] 

let a = [| 1 .. 100000 |];; 

for i in 1 .. 100 do  ß takes 885ms

ignore(l |> list.map (fun n -> n));;

for i in 1 .. 100 do  ß takes 109ms

ignore(a |> array.map (fun n -> n));;

如果需要高效處理大型資料集,通常陣列更好。在大多數情況下,首要目的應該使用清晰和簡單的**,函式式列表通常會有更好的可讀性。

我們前面的例子表明,雖然可以使用陣列上提供的一些基本操作,但仍經常要自己寫一些類似的操作。清單 10.13 是乙個函式,以函式風格處理陣列:引數為乙個陣列,根據輸入計算並返回乙個新的陣列。這個函式常用於「平滑」或「模糊」值陣列,在新陣列中的每個值與原有的值和它兩側的值相對應。

清單 10.13 模糊化陣列的函式式方法 (f#)

let blurarray (arr:int) = 

letres = array.create arr.length 0 

res.[0] <- (arr.[0] + arr.[1]) / 2                                  | [1]

res.[arr.length-1] <- (arr.[arr.length-2] + arr.[arr.length-1]) / 2  |

for iin 1 .. arr.length - 2 do     [1]

res.[i] <- (arr.[i-1] + arr.[i] + arr.[i+1]) / 3 

res函式首先建立用於儲存結果的陣列,大小與輸入陣列相同;然後,計算出新陣列的第乙個元素的值和最後乙個元素的值[1](這是兩個元素的平均值),這些值的計算與陣列的其餘部分是分開的,因為它們是邊界,模式與其餘部分不在一樣;最後,遍歷陣列中間的元素,取三個值的平均值,作為結果寫入新的陣列中。

函式內部使用可變模式(mutation),在開始時,建立的陣列用零填充,後來,把計算值寫到這個陣列中。這種可變性從外部是不可見的,到呼叫者能夠使用陣列的時候,我們已經完成了變。當我們使用這個函式時,完全可以放心地使用所有通常的函式技術:

> let ar = array.init 10 (fun _ ->rnd.next(20));;  ß 初始化隨機數組

val ar : int = [|14; 14; 4; 16; 1; 15;5; 14; 7; 13|]

> ar |> blurarray;;  ß 對陣列進行模糊化一次

val it : int = [|14; 10; 11; 7; 10; 7;11; 8; 11; 10|]

> ar |> blurarray |> blurarray|> blurarray;;    ß 使用管道對陣列進行模糊化三次

val it : int = [|7; 8; 9; 9; 9; 9; 9; 9;8; 8|]

blurarray 函式的型別是int -> int,這使它成為可組合的。第二個命令,我們使用管道運算子,把隨機生成的陣列作為輸入,傳送給函式,f# 互動控制台自動輸出結果。最後乙個命令表明,我們還能夠連續呼叫函式幾次,同樣的方法,我們在列表的 map 或 filter 操作上使用過。

我們可能想擴充套件這個例子用來處理影象,把 blurarray 函式轉換成為真正的、能處理點陣圖的模糊濾鏡。如果想嘗試一下,還需要使用 array2d 模組,它有處理二維陣列的函式,以及讀取、寫入圖形資料的 .net 點陣圖類,比如 getpixel 和 setpixel。到第十四章,我們還會回來到這個問題,討論使用並行化更有效地執行操作。

在看過在 f# 中優美地使用陣列以後,我們將把注意力放回到 c# 中。所有的 c# 程式設計師都知道使用陣列的基本知識,而我們感興趣的是,能使用函式風格寫出處理陣列的 c# **。

10 2 3 2 在 C 中以函式風格使用陣列

10.2.3.2 在 c 中以函式風格使用陣列 由於有了 linq to object,在 c 3.0 中,我們已經可以使用許多函式結構來處理陣列。大多數 linq 運算子不返回陣列 如果在陣列上呼叫 enumerable.select,結果將返回 ienumerable。在某些情況下,我們還是願意...

Spring以註解方式使用aop

7.spring以註解方式使用 xmlversion 1.0 encoding utf 8 beans xmlns xsi xmlns xmlns context xmlns aop xsi schemalocation spring beans 4.2.xsd spring context 4.2...

JavaScript陣列的三種定義方式以及使用

陣列的定義 1.陣列的屬性以及方法 1 常用屬性 length 返回陣列中元素的個數 2 常用方法 方 法說 明join 將陣列中的元素組合成字串 reverse 顛倒陣列元素的順序,使第乙個元素成為最後乙個,而最後乙個元素成為第乙個 sort 對陣列元素進行排序 陣列的常用方法 function ...