2

次のように、F# で独自の論理暗黙演算子を定義してみました。

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

しかし、実際に試してみると、短絡ORを実装していないようです

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

後件を評価するべきではありませんが、それはまだです

ここで何が問題なのか誰にもわかりますか?VS 2012 で F# を実行しています

4

4 に答える 4

10

を記述するときa --> b、実際に行われているのは、-->2 つのパラメーターでa呼び出される関数の呼び出しbです。演算子の構文は、単なる構文糖衣です。

関数を呼び出す前に、ランタイムはその関数のすべての引数を評価する必要があります。したがって、 を呼び出す前に-->、まず評価falseし、次に を評価し(2/0)=1ます。この最後の式を評価すると、例外がスローされます。あなたの暗黙の関数は決して呼び出されません!

Haskell などの他の言語では、遅延評価があります。つまり、関数のパラメーターは、関数内で実際にアクセスされた場合にのみ評価されます。値を渡すのではなく、その値に評価される関数、または呼び出されたサンクを渡すことで、それをシミュレートできます。

この種の機能を F# (サンク) で実装するには、関数を少し変更する必要があることに注意してください。John Palmer の例のように、サンクを呼び出して値を取得する必要があります。

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

暗黙の演算子q()の定義における関数呼び出しに注目してください。2 番目の引数で thunk を指定しないと、もう機能しません!

于 2013-07-25T21:50:55.937 に答える
3

RHS の評価は、関数に渡される前に発生する必要があるため、ゼロ除算が非常に早く発生するため、このようなガードは機能しません。

代わりにそのような関数を渡すことができます

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

false --> (fun _ -> ((2/0)=1))
于 2013-07-25T21:51:08.280 に答える
2

F# には厳密な評価戦略があります。これは、引数が関数に渡される前に評価されることを意味します。このため、2/0=1は関数に渡される前に評価される-->ため、 の短絡は||の評価に影響しません。これは の2/0=1前に評価されるためです||

-->関数 (演算子)を変換して、値ではなく名前で引数を取るようにする必要があります。実際には、これは次のいずれ() -> 'Tかを取ることを意味しLazy<'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 @>
于 2013-07-26T06:35:57.783 に答える