10-列舉型別和流
10-列舉型別和流
列舉物件
積極vs懶惰
流
10.1-列舉型別
Elixir提供了列舉型別(enumerables)的概念,使用Enum模組操作它們。我們已經介紹過兩種列舉型別:列表和圖。
iex> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]
iex> Enum.map(%{1 => 2, 3 => 4}, fn {k, v} -> k * v end)
[2, 12]
Enum模組為列舉型別提供了大量函式來變化,排序,分組,過濾和讀取元素。 Enum模組是開發者最常用的模組之一。
Elixir還提供了範圍(range):
iex> Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]
iex> Enum.reduce(1..3, 0, &+/2)
6
因為Enum模組在設計時為了適用於不同的資料型別,所以它的API被限制為多資料型別適用的函式。 為了實現某些操作,你可能需要針對某型別使用某特定的模組。 比如,如果你要在列表中某特定位置插入一個元素,要用List模組中的List.insert_at/3函式。而向某些型別內插入資料是沒意義的,比如範圍。
Enum中的函式是多型的,因為它們能處理不同的資料型別。 尤其是,模組中可以適用於不同資料型別的函式,它們是遵循了Enumerable協議。 我們在後面章節中將討論這個協議。下面將介紹一種特殊的列舉型別:流。
10.2-積極vs懶惰
Enum模組中的所有函式都是積極的。多數函式接受一個列舉型別,並返回一個列表:
iex> odd? = &(rem(&1, 2) != 0)
#Function<6.80484245/1 in :erl_eval.expr/5>
iex> Enum.filter(1..3, odd?)
[1, 3]
這意味著當使用Enum進行多種操作時,每次操作都生成一箇中間列表,直到得出最終結果:
iex> 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum
7500000000
上面例子是一個含有多個操作的管道。從一個範圍開始,然後給每個元素乘以3。 該操作將會生成的中間結果是含有100000個元素的列表。 然後我們過濾掉所有偶數,產生又一個新中間結果:一個50000元素的列表。 最後求和,返回結果。
這個符號的用法似乎和F#中的不一樣啊...
作為一個替代,流模組提供了懶惰的實現:
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum
7500000000
與之前Enum的處理不同,流先建立了一系列的計算操作。然後僅當我們把它傳遞給Enum模組,它才會被呼叫。流這種方式適用於處理大量的(甚至是無限的)資料集合。
10.3-流
流是懶惰的,比起Enum來說。 分步分析一下上面的例子,你會發現流與Enum的區別:
iex> 1..100_000 |> Stream.map(&(&1 * 3))
#Stream<[enum: 1..100000, funs: [#Function<34.16982430/1 in Stream.map/2>]]>
流操作返回的不是結果列表,而是一個資料型別---流,一個表示要對範圍1..100000使用map操作的動作。
另外,當我們用管道連線多個流操作時:
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?)
#Stream<[enum: 1..100000, funs: [...]]>
流模組中的函式接受任何列舉型別為引數,返回一個流。
流模組還提供了建立流(甚至是無限操作的流)的函式。
例如,Stream.cycle/1
可以用來建立一個流,它能無限週期性列舉所提供的引數(小心使用):
iex> stream = Stream.cycle([1, 2, 3])
#Function<15.16982430/2 in Stream.cycle/1>
iex> Enum.take(stream, 10)
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
另一方面,Stream.unfold/2
函式可以生成給定的有限值:
iex> stream = Stream.unfold("hełło", &String.next_codepoint/1)
#Function<39.75994740/2 in Stream.unfold/2>
iex> Enum.take(stream, 3)
["h", "e", "ł"]
另一個有趣的函式是Stream.resource/3
,它可以用來包裹某資源,確保該資源在使用前開啟,在用完後關閉(即使中途出現錯誤)。--類似C#中的use{}關鍵字。
比如,我們可以stream一個檔案:
iex> stream = File.stream!("path/to/file")
#Function<18.16982430/2 in Stream.resource/3>
iex> Enum.take(stream, 10)
這個例子讀取了檔案的前10行內容。流在處理大檔案,或者慢速資源(如網路)時非常有用。
一開始Enum和流模組中函式的數量多到讓人氣餒。但你會慢慢地熟悉它們。 建議先熟悉Enum模組,然後因為應用而轉去流模組中那些相應的,懶惰版的函式。
相關文章
- 列舉型別型別
- ENUM列舉型別型別
- Java - Enum 列舉型別Java型別
- Rust的列舉型別EnumRust型別
- java中的列舉型別Java型別
- JavaSE基礎:列舉型別Java型別
- 【C++】資料型別-列舉型C++資料型別
- 【四】使用列舉和結構來建立值型別型別
- C++ 列舉型別介紹C++型別
- 列舉型別分享 第五節型別
- java基礎(十一) 列舉型別Java型別
- JPA不識別MySQL的列舉型別MySql型別
- OC中列舉寫法 以及 字串型別列舉實現探索字串型別
- 補充:C語言列舉型別C語言型別
- 簡單探討TypeScript 列舉型別TypeScript型別
- C語言 列舉資料型別C語言資料型別
- 列舉型別在JPA中的使用型別
- java中的列舉型別學習Java型別
- Java基礎教程(15)–列舉型別Java型別
- 物聯網學習教程—列舉型別型別
- PHP 列舉型別的管理與設計PHP型別
- Java列舉型別enum的詳解及使用Java型別
- @RequestBody中列舉型別值不匹配報錯型別
- C++特別數的和(列舉)C++
- Java 列舉、JPA 和 PostgreSQL 列舉JavaSQL
- Enum列舉型別實戰總結,保證有用!型別
- TypeScript 中列舉型別的理解?應用場景?TypeScript型別
- 列舉和列舉的取值範圍
- 介面和列舉在方法中的區別
- 別再說Python沒有列舉型別了,好好看看Python型別
- 資料結構複習-01enum列舉型別資料結構型別
- 結構體-簡單列舉型別——植物與顏色結構體型別
- sizeof和strlen計算陣列型別和指標型別字串陣列型別指標字串
- Java 集合列舉泛型(一)Java泛型
- 擴充了個新業務列舉型別,資損了型別
- 求你了,不要再在對外介面中使用列舉型別了!型別
- 類中的結構體或列舉等型別的前置宣告結構體型別
- C語言列舉型別所佔位元組大小例項解析C語言型別
- Swift 5.0 值得關注的特性:增加 Result<T, E: Error> 列舉型別SwiftError型別