12 1 3 使用 F 序列表示式

2021-06-01 13:06:41 字數 3163 閱讀 8567

12.1.3 使用 f# 序列表示式

在 c# 中的迭代器是很舒服,因為它們允許我們在普通的 c# 方法中寫複雜的** (一種型別,實現  ienumerable/ienumerator介面) 。開發人員寫的**使用標準的 c# 功能,比如迴圈,唯一的改變只是我們可以使用一種新的語句,來做一些非標準的東西。這個新語句用 yield return 來表示(或者 yield break 表示終止序列),非標準的行為返回乙個值,作為序列中的下乙個元素。然後,在需要的時候使用 movenext 方法訪問序列(最後,逐個元素進行) 。在 f# 中的序列表示式是類似的:使用一種等效於 yield return 的結構。

寫序列表示式

在 c# 中,我們可以自動使用迭代器,在實現返回 ienumerable、ienumerator,或對應的非泛型的方法時。f# 序列表示式使用 seq 識別符號顯式標記,而且不必要使用方法體或函式體。正如其名字所暗示的,序列表示式是表示式的不同型別,我們可以在**中的任意位置使用。清單 12.2 演示了如何建立乙個簡單的序列,使用此語法。

listing 12.2 introducing sequence expression syntax (f# interactive)

> let nums =

seq ;;

val nums : seq

當寫序列表示式時,我們把生成序列的整個 f# 表示式括在乙個 seq 塊中。這個塊使用大括號,用 seq 識別符號 [1] 表示,編譯器可以把這個塊主體解釋為序列表示式。還有其他可能的識別符號指定其他選擇性的工作流,在後面,我們將會看到。在 seq 的情況下,這個塊把整個表示式轉換為延遲生成的序列,從這個值的型別推斷可以看到。

序列表示式的主體可以包含具有特殊意義的語句。類似於 c#,有乙個語句,返回這個序列的乙個元素。在 f# 中,使用 yield 關鍵字。主體也可以包含其他標準的 f# 結構,比如值繫結,甚至是有***的呼叫。

類似於 c#,序列表示式的主體是延遲執行的。當我們建立序列值(在我們前面的示例中,值 nums)時,序列表示式的主體並不執行。這僅當我們訪問這個序列的元素時發生,每次我們訪問乙個元素時,序列表示式**才執行,直到下乙個 yield 語句。在 c# 中,在乙個迭代器訪問元素的最常見方式,是使用 foreach 迴圈。在下面的 f# 示例中,我們將使用 list.ofseq 函式,把這個序列轉換為乙個不可變的 f# 列表:

> nums |> list.ofseq;;

second..

val it : int list = [11; 12]

返回的列表包含序列生成兩個元素的。這意味著,必須計算整個表示式,中途執行 printfn,這就是為什麼輸出中包含來自序列表示式的列印行。如果我們只從序列中取乙個元素,該序列表示式將只計算到第乙個 yield 呼叫,字串不會列印出來:

> nums |> seq.take 1 |> list.ofseq;;

val it : int list = [11]

我們將使用乙個來自seq 模組的序列處理函式,從這個序列中只取乙個元素。take 函式返回乙個新的序列,取指定數量的元素(這個示例中是 1),然後終止。當我們將它轉換為 f# 列表時,就得到乙個只包含乙個元素的列表,且不呼叫 printfn 函式。

當我們實現乙個序列表示式時,可能會遇到這樣的情況,表示式的主體太長。在這種情況下,最自然的做法就是,將它拆分成幾個生成序列部件的函式。如果序列使用多個資料來源,我們可能希望讀取資料的**,在單獨的函式中。到目前為止,一切順利,但是,還有乙個問題,把從不同函式返回的序列組合起來。

組合序列表示式

在 c# 中的 yield return 關鍵字只允許返回乙個元素,因此,如果要在 c# 中,從使用迭代器實現的方法產生整個序列的話,就必須使用 foreach 迴圈,遍歷序列中的所有元素,乙個乙個地產生元素。這可以工作,但效率低下,特別是如果以這種方式生成幾個巢狀的序列。在函式式程式設計中,可組合性是乙個更重要的方面,所以,f# 允許我們組合序列,從序列表示式產生整個序列,使用專門的語言表達結構:yield!(通常發音 yield-bang)。清單 12.3 演示了以三種不同的方法生成乙個城市的序列。

listing 12.3 composing sequences from different sources (f# interactive)

> let capitals = [ "paris"; "prague" ];;

val capitals : string list

> let withnew(name) =

seq ;;

val withnew : string -> seq

> let allcities =

seq ;;

val allcities : seq

> allcities |> list.ofseq;;

val it : string list = ["oslo"; "paris"; "prague"; "york"; "new york"]

清單 12.3 建立兩個不同的資料來源。第乙個是 f# 列表,包含兩個首都,這個值的型別是 list,但是,因為 f# 列表實現了 seq <'a> 介面,因此,可以在後面的**將它當作乙個序列來使用。第二個資料來源是乙個函式,它生成包含兩個元素的序列。下面的一段**演示如何將這兩個資料來源聯接成乙個序列。首先,我們使用 yield 語句返回乙個值,接著,使用 yield! 結構返回 f# 列表中的所有元素,最後,呼叫 withnew 函式(返回乙個序列),返回該序列的所有元素。從這裡可以看出,可以在乙個序列表示式混合使用兩種產生元素的方法。

就像 yield 一樣,yield! 結構也是延遲返回元素。就是說,當**到達呼叫 withnew 函式時,這個函式才呼叫,而且,只返回表示該序列的乙個物件。如果我們在函式中寫一些**,放在 seq 塊之前,它會在這一點上執行,但是,seq 塊的主體並不會執行。它要到 withnew 函式返回之後才發生,因為,我們需要生成的下乙個元素。當執行到達第乙個 yield 結構時,它將返回該元素,並將控制權返回到呼叫者。呼叫者然後執行其他操作,當呼叫者請求另乙個元素時,序列的執行才恢復。

我們一直專注於序列表示式的語法,但是,可能聽起來相當尷尬,直到我們開始使用。當使用序列表示式時,有幾個模式是共同的,讓我們看兩個。

1 你可能會感到奇怪,我們把 seq 稱為識別符號,而不叫關鍵字,後面你會看到,這就是乙個識別符號(我們甚至自己定義),而不是 f# 語言中內建的專門的關鍵字。seq 識別符號也並不是由 seq <'a> 型別自動定義的。名字是一樣的,但是在這裡,seq 識別符號是由 f# 庫定義不同的符號。

12 1 3 使用 F 序列表示式

12.1.3 使用 f 序列表示式 在 c 中的迭代器是很舒服,因為它們允許我們在普通的 c 方法中寫複雜的 一種型別,實現 ienumerable ienumerator介面 開發人員寫的 使用標準的 c 功能,比如迴圈,唯一的改變只是我們可以使用一種新的語句,來做一些非標準的東西。這個新語句用 ...

表示式 表示式樹 表示式求值

總時間限制 1000ms 記憶體限制 65535kb 描述 眾所周知,任何乙個表示式,都可以用一棵表示式樹來表示。例如,表示式a b c,可以表示為如下的表示式樹 a b c 現在,給你乙個中綴表示式,這個中綴表示式用變數來表示 不含數字 請你將這個中綴表示式用表示式二叉樹的形式輸出出來。輸入輸入分...

OGNL表示式使用

訪問值棧中的action的普通屬性 username 訪問值棧中物件的普通屬性 get set方法 wrong 訪問值棧中物件的普通屬性 get set方法 訪問值棧中物件的普通方法 訪問值棧中物件的普通方法 訪問值棧中action的普通方法 訪問靜態方法 訪問靜態屬性 訪問math類的靜態方法 訪...