2013-07-25 53 views
2

我試圖定義我自己的邏輯意味着運營商在F#如下奇怪的行爲界定意味着運營商在F#

let (-->) p q = (not p) || q 

但是當我真正嘗試它,它似乎不被執行短路或

> false --> ((2/0)=1);; 
System.DivideByZeroException: Attempted to divide by zero.at <StartupCode$FSI_0015>[email protected]() 
Stopped due to error 
> 

它不應該被評估隨之而來的卻是

任何人都可以看看有什麼不對嗎?我運行F#在VS 2012

回答

10

當你寫a --> b,究竟怎麼回事上是一個函數的調用稱爲-->有兩個參數,ab運算符語法只是一些語法糖。

在調用函數之前,運行時必須評估該函數的所有參數。所以,在致電-->之前,它首先評估false然後(2/0)=1。評估這最後一個表達式時,它會拋出一個異常。你的意味着函數永遠不會被調用!

在其他一些語言中,比如Haskell,你有懶惰的評估。也就是說,函數的參數只有在函數內部實際訪問時才被評估。你可以通過不傳遞一個值來模擬這個值,但是可以傳遞一個函數來計算該值,或者稱爲thunk

需要注意的是,爲了實現這種在F#(的thunk)功能,該功能有稍微修改:它必須呼叫形實轉換來獲得它的價值,就像約翰·帕爾默給出的例子:

let --> p q = (not p) || q() 
let thunk = (fun _ -> ((2/0)=1)) 
false --> thunk 

注意函數調用q()的定義意味着運營商。如果你沒有在第二個參數上給它一個thunk,它就不會工作了!

3

它傳遞給函數之前,所以除零很早所以這種保護將無法正常工作時,必須發生RHS的評價。

您可以改爲通過像這樣

let --> p q = (not p) || q() 

false --> (fun _ -> ((2/0)=1)) 
6

功能,您還可以使用lazy評估需求的第二個參數:

let (-->) p (q: Lazy<bool>) = (not p) || q.Force() 

,你可以將其稱爲:

false --> (lazy (2/0=1)) 
2

F#有嚴格的評估策略。這意味着參數在傳遞給函數之前被評估。因此,2/0=1在傳遞到您的-->函數之前進行評估,因此||的短路不會影響2/0=1的評估,因爲在||之前評估該評估。

您需要將您的函數(-->運算符)轉換爲按名稱而不是按值的參數。實際上,這意味着,使其採取任何() -> 'TLazy<'T>

let (-->) p q = (not <| p()) || q() 

> (fun() -> false) --> (fun() -> 2/0=1);; 
true 

,或者使用內置的lazy結構。這種方式可以達到同樣的效果(前提是您的計算不依賴於副作用)有位更簡潔的語法:

let (-->) (p : Lazy<_>) (q : Lazy<_>) = (not <| p.Force()) || q.Force() 

> lazy false --> lazy (2/0 = 1) 
true 

它的工作,但它看上去並不是很方便。不幸的是,我認爲有擺脫fun() -> ...沒有簡單的方法,但我認爲你可以通過定義一些常量有點減少樣板:

let True, False = (fun() -> true), (fun() -> false) 

爲進一步減少每個參數創建拉姆達的樣板,你可以嘗試使用代碼報價(和圖書館像Unquote以評估它們):

let (-->) p q = (not (eval p)) || (eval q) 

<@ false @> --> <@ 2/0 = 1 @> 
+0

@ MisterMetaphor - 這無關與評價的順序;這與渴望與懶惰評估是正交的。 –

+0

@OnorioCatenacci,你說得對,我想我的意思是評估策略。謝謝。 – MisterMetaphor