12.6 實現選項的計算表示式
在 12.4 節中,我們用選項值作為示例,介紹了用 linq 查詢和 f# 的計算表示式建立非標準計算的概念。我們所寫的**處理選項值,有自定義的值繫結來讀取實際值,就好像是乙個標準值。我們已經看到如何轉換計算表示式,知道我們的 bind 成員會接收乙個值,和乙個 lambda 函式。我們只想執行這個 lambda 表示式,有我們的選項型別計算表示式,如果這個值是 some(x) 而不是 none 的話。在後一種情況下,我們可以立即返回 none。
要執行較早的例子,我們需要在 c# 中實現 linq 查詢運算子,在 f# 中實現選項計算生成器。再次,我們先從 f# 版本開始。清單 12.21 顯示了有兩個成員的 f# 物件型別。我們已經在第 6 章中實現了 option.bind 函式,但我們在這裡會重新實現它,提醒你典型的 bind 操作做什麼。
listing 12.21 computation builder for option type (f#)
type optionbuilder() =
member x.bind(opt, f) =
match opt with
| some(value) -> f(value)
| _ –> none
member x.return(v) = some(v)
let option = new optionbuilder()
return 成員取乙個值作為引數值,必須返回乙個計算型別的值。在我們的示例中,計算的型別是 option<'a>,所以,把實際值打包到 some 識別器內。
為了在 c# 中使用查詢語法寫對應的**,我們就需要為我們在第 5 章中定義的 option型別,實現 select 和 selectmany 方法。清單 12.22 實現了兩個額外的擴充套件方法,使我們可以在查詢表示式中使用選項。這次,我們會使用我們在第六章中寫的擴充套件方法,使**更加簡單。
listing 12.22 query operators for option type (c#)
static class optionextensions
public static optionselectmany
(this optionsource,
func> valueselector,
funcresultselector)
}select 方法應該把給定的函式應用到由給定選項值所拾的值上,如果它包含實際值。然後,它再把這個結果打包到選項型別中。在 f# 中,函式稱為 option.map,我們給這個 c# 方法使用類似的名字(map)。如果我們第一次看 linq,開始可能會呼叫 select 方法,但是,最簡單的解決方案被新增到新方法 map 中。
selectmany 會更複雜。它類似於 bind 操作,但是,它需要使用額外指定的函式,作為第三個引數值,來格式化操作的結果除外。在第 6 章中,我們寫過 c# 版本的 bind 操作,因此,在這個實現中,我們可以使用 bind 擴充套件方法。要呼叫格式化函式 resultselector,我們需要兩個引數值:乙個是由選項攜帶原始值,另乙個是由繫結函式 (命名為 selector) 產生的值。我們可以在處理的末尾,新增對 map 的呼叫,執行此操作,但是,我們需要把這個呼叫放在 lambda 函式內部,給 bind 方法。這是因為,我們還需要訪問來自源的原始值。在 lambda 函式內部,原始值是在這個範圍內 (名為 sourcevalue 的變數),因此,我們可以使用它,和新的值一起,即分配給變數 resultvalue。
這個實現是有點棘手,但它表明,在函式式程式設計中,很多東西可以由我們已有的組合而成。如果我們試圖實現自己的,可以看到,在這裡,型別是寶貴的助手。你可能只從使用 bind 方法開始,但是,可能看到的型別並不匹配。如果看過哪些函式是可用的,你會看到什麼型別不相容,你會發現,為了獲得正確的型別,需要新增什麼。我們自己重複的風險在於:函式式程式設計中的型別是重要得多,告訴你更多有關程式的正確性問題。
使用新的擴充套件方法,我們可以執行 12.3 節中的示例。在 f# 中,我們並沒有為 yield 和 for 基元提供實現,所以,只使用 return 和 let! 版本返回將工作。這是故意的,因為,這些第一組基元更適合處理各種形式的序列的計算。我們仍然需要實現 tryreadint 方法 (類似於 f# 的函式)。這些是真簡單,因為,只要從控制台讀取一行,並嘗試解析,然後,當字串是數字時,返回 some,其它則返回 none。
肯定和可能一元運算
我們前面看到的兩個例子,在 haskell 世界中是眾所周知的。第乙個示例是肯定一元運算(identity monad),因為一元型別與值的實際型別相同,只打包在命名型別中。第二個示例是可能一元運算(maybe monad),因為,maybe 是 haskell 的型別名,對應於 f# 中的 option<'a> 型別。
第乙個主要是玩具示例,演示了在實現計算時,我們需要做什麼;但是,第二個示例在寫組合大量操作的**時,是有用的,其中每個可能失敗。當你分析這兩個例子時,可以看到,一元型別是如何的重要。一旦我們理解了型別,就會知道,是什麼讓計算非標準。
到目前為止的例子已經有點抽象,下一節,會有很多更具體的示例;在我們的**中新增自動日誌。
12 6 實現選項的計算表示式
12.6 實現選項的計算表示式 在 12.4 節,我們用選項值作為示例,介紹了用 linq 查詢和 f 計算表示式建立非標準計算的概念,處理選項值的 有自定義的值繫結讀取實際值,如同標準值。既然我們已經知道如何轉換計算表示式,也就知道我們的 bind 成員會接收值和 lambda 函式。因為我們處理...
棧實現表示式計算
讓index 1,並盤算是否掃瞄到expression最後 index if index expression.length 掃瞄完畢,就順序從數棧和符號棧中pop出相應的數和符號,並執行 while true num1 numstack.pop num2 numstack.pop oper ope...
棧實現表示式計算
原理 數 入棧 入棧 運算子出棧,直到 和 匹配 運算子 當前符優先順序 棧頂符優先順序 入棧 當前符優先順序 棧頂符優先順序 棧內運算子出棧,運算後進棧,再比較 其中 優先順序大於 如下 先建compute.h標頭檔案 pragma once double compute char str 計算 ...