Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# F中的奇怪行为#_F# - Fatal编程技术网

F# F中的奇怪行为#

F# F中的奇怪行为#,f#,F#,我尝试在F#中定义自己的逻辑隐含运算符,如下所示 let (-->) p q = (not p) || q 但当我实际尝试时,它似乎没有实现短路或短路 > false --> ((2/0)=1);; System.DivideByZeroException: Attempted to divide by zero.at <StartupCode$FSI_0015>.$FSI_0015.main@() Stopped due to error > >fal

我尝试在F#中定义自己的逻辑隐含运算符,如下所示

let (-->) p q = (not p) || q
但当我实际尝试时,它似乎没有实现短路或短路

> false --> ((2/0)=1);;
System.DivideByZeroException: Attempted to divide by zero.at <StartupCode$FSI_0015>.$FSI_0015.main@()
Stopped due to error
> 
>false-->((2/0)=1);;
System.DivideByZeroException:尝试除以零。在$FSI_0015.main@()
由于错误而停止
> 
它不应该评估结果,但它是

有人能看出这里出了什么问题吗?我在VS 2012中运行F#

当您编写
a-->b
时,真正发生的是调用一个名为
-->
的函数,该函数有两个参数,
a
b
。运算符语法只是一些语法糖

在调用函数之前,运行时必须计算该函数的所有参数。因此,在调用
-->
之前,它首先计算
false
,然后计算
(2/0)=1
。计算最后一个表达式时,它会引发异常。你的函数永远不会被调用

在其他一些语言中,例如Haskell,您有延迟计算。也就是说,只有在函数内部实际访问函数的参数时,才会对其求值。您可以通过不传递值,而是传递计算结果为该值的函数或调用时的函数来模拟该情况

请注意,要在F#(thunks)中实现此类功能,必须对函数进行轻微修改:它必须调用thunk以获取其值,就像John Palmer给出的示例:

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

注意隐式运算符定义中的函数调用
q()
。如果你不在第二个论点上重击一下,它就不会再起作用了

必须在将RHS传递给函数之前对其进行求值,因此被零除的时间很早,因此这样的保护将不起作用

您可以像这样传递函数

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

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

您还可以使用
lazy
根据需要计算第二个参数:

let (-->) p (q: Lazy<bool>) = (not p) || q.Force()
F#有严格的评估策略。这意味着参数在传递给函数之前要进行求值。因此,
2/0=1
在传递给
-->
函数之前进行求值,因此
|
的短路不会影响
2/0=1
的求值,因为这是在
|
之前进行求值的

您需要将函数(
-->
运算符)转换为按名称而不是按值获取其参数。实际上,这意味着需要
()->'T
Lazy…
,但我认为您可以通过定义一些常量来简化样板文件:

let True, False = (fun () -> true), (fun () -> false)
为了进一步简化为每个参数创建lambda的样板,您可以尝试使用代码引用(以及类似于对其求值的库):

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

@MisterMetaphor——这与评估顺序无关;这是关于渴望与懒惰的评估,这与顺序是正交的。@OnorioCatenacci,你是对的,我想我指的是评估策略。谢谢
let (-->) p q = (not <| p()) || q()

> (fun () -> false) --> (fun () -> 2/0=1);;
true
let (-->) (p : Lazy<_>) (q : Lazy<_>) = (not <| p.Force()) || q.Force()

> lazy false --> lazy (2 / 0 = 1)
true
let True, False = (fun () -> true), (fun () -> false)
let (-->) p q = (not (eval p)) || (eval q)

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