2011-07-13 105 views
8

我剛剛開始F#所以請善待,如果這是基本的。F#懶惰評估與非懶惰

我讀過一個標記爲lazy的函數只計算一次,然後緩存。例如:

let lazyFunc = lazy (1 + 1) 
let theValue = Lazy.force lazyFunc 

比起這個版本這將每次叫時間實際運行:

let eagerFunc = (1 + 1) 
let theValue = eagerFunc 

此基礎上,應所有功能進行懶惰?你什麼時候不想?這是來自「Beginning F#」一書中的內容。

+0

這是什麼版本的F#?我有一個懶惰行事的序列,但並沒有公開這樣創建。我試圖強制它完成。 – octopusgrabbus

回答

13

首先,它可能是有益的注意,所有已定義的東西是一個功能 - eagerFunctheValue是類型的值intlazyFuncLazy<int>類型的值。鑑於

let lazyTwo = lazy (1 + 1) 

let eagerTwo = 1 + 1 

表達1 + 1評估一次以上,無論你有多少次使用eagerTwo。所不同的是1 + 1將被評估恰好一次時限定eagerTwo,但將被評估至多一次時lazyTwo使用(它會在第一時間被評價爲Value屬性被訪問,並然後緩存以便進一步使用Value不需要重新計算它)。如果lazyTwoValue從未訪問,那麼它的身體1 + 1永遠進行評估。

通常情況下,在F#等嚴格語言中使用惰性值時看不到太多好處。由於訪問Value屬性需要檢查值是否已經計算,因此它們會添加少量開銷。如果您有類似let lazyValue = lazy someVeryExpensiveCalculationThatMightNotBeNeeded()的東西,它們可能會爲您節省一些計算費用,因爲只有在實際使用該值時纔會發生昂貴的計算。他們也可以使一些算法終止,否則不會,但這不是F#中的主要問題。例如:

// throws an exception if x = 0.0 
let eagerDivision x = 
    let oneOverX = 1.0/x 
    if x = 0.0 then 
     printfn "Tried to divide by zero" // too late, this line is never reached 
    else 
     printfn "One over x is: %f" oneOverX 

// succeeds even if x = 0.0, since the quotient is lazily evaluated 
let lazyDivision x = 
    let oneOverX = lazy (1.0/x) 
    if x = 0.0 then 
     printfn "Tried to divide by zero" 
    else 
     printfn "One over x is: %f" oneOverX.Value 
+0

不使用'()'暗示它是一個函數嗎?無論如何,好點。你是對的,我的例子是非常微不足道的。 – Yuck

+0

@Yuck - 括號在不同的情況下意味着不同的事物。當你像'(1 + 1)'一樣使用它們時,它們只是用來表示分組和優先。如果你定義了'let eagerFunc()= ...',那麼括號表明'eagerFunc'是一個函數,但是請注意這與你寫的不同。 – kvb

6

如果函數執行有副作用,看到的副作用每個函數被調用的時間是很重要的(說包裝了一個I/O功能),你不會希望它是懶惰。

也有這麼瑣碎的是,執行他們每個時間比緩存value--

5

let eagerFunc = (1 + 1)快是讓約束力,並且將只執行一次的功能。 let eagerFunc() = (1 + 1)是接受unit(無)並返回int的函數。它會在每次調用時執行。從某種意義上說,每個函數都是懶惰的,也就是說,它只在被調用時執行。然而,lazy關鍵字(和它返回的System.Lazy)將最多執行一次賦予它的表達式/函數。隨後調用Value屬性將返回緩存的結果。這在計算價值昂貴時很有用。

許多功能將不適合與lazy一起使用,因爲它們要麼是非確定性的(可能會在每次調用時返回不同的結果)或參數化的。當然,可以使用這些函數的完全應用(爲每個參數提供值)版本,但通常需要可變性。