在这个bind()参数中省略unit参数时,我应该看到区别吗?

Ash*_*kar 6 .net f#

在这个来自F Sharp Programming Wikibook计算表达式部分的 F#代码中:

let addThreeNumbers() =
    let bind(input, rest) =
        match System.Int32.TryParse(input()) with
        | (true, n) when n >= 0 && n <= 100 -> rest(n)
        | _ -> None

    let createMsg msg = fun () -> printf "%s" msg; System.Console.ReadLine()

    bind(createMsg "#1: ", fun x ->
        bind(createMsg "#2: ", fun y ->
            bind(createMsg "#3: ", fun z -> Some(x + y + z) ) ) )
Run Code Online (Sandbox Code Playgroud)

当我转换input()inputcreate Msg msg来自fun () -> printf "%s" msg; System.Console.ReadLine()printf "%s" msg; System.Console.ReadLine():

let addThreeNumbers() =
    let bind(input, rest) =
        match System.Int32.TryParse(input) with
        | (true, n) when n >= 0 && n <= 100 -> rest(n)
        | _ -> None

    let createMsg msg = printf "%s" msg; System.Console.ReadLine()

    bind(createMsg "#1: ", fun x ->
        bind(createMsg "#2: ", fun y ->
            bind(createMsg "#3: ", fun z -> Some(x + y + z) ) ) )
Run Code Online (Sandbox Code Playgroud)

当我在dotnetfiddle.net上运行它时,程序似乎表现完全一样.这只是一个边缘情况,其中实际上不需要单位参数来延迟计算,因为它们依赖于来自Console.ReadLine()的用户输入,或者修改后的版本不正确,或者它在其他方面表现不同还没有注意到?

Fyo*_*kin 7

在实践中

你是对的.在这种情况下,input计算"延迟" 的事实是完全多余的,因为它在第一行上无条件地"未延迟" bind,因此没有可能的情况,延迟计算将在以后运行或不在所有.

一个微妙的区别(在实践中无关紧要)是:在原始代码中,Console.ReadLine从内部调用bind,但在修改后的代码中,之前Console.ReadLine调用,然后将其结果传递给. bindbind

如果bind某种程度上更复杂(例如,如果它有一个try .. withinput()或类似的东西),那么这种差异将是重要的.然而,事实上,推迟不会增加任何东西.


但理论上

另一种可以看到差异的方法是,如果您"提前"准备阅读操作,而不是在现场创建它们:

let msg1 = createMsg "#1: "
let msg2 = createMsg "#2: "
let msg3 = createMsg "#3: "
bind(msg1, fun x ->
    bind(msg2, fun y ->
        bind(msg3, fun z -> Some(x + y + z) ) ) )
Run Code Online (Sandbox Code Playgroud)

使用此代码,原始文件bind可以正常工作,但是您的修改bind将导致所有三个输入每次都发生,而不是停止在第一个无效输入上.

虽然表面看起来是随机的,但它实际上说明了程序设计中的一个重要考虑因素:评估执行之间的差异.简而言之,"评估"可以理解为"为工作做准备",而"执行"可以理解为"实际做工作".在上面的代码片段中,该行let msg1 =表示对输入读取操作的评估,而调用bind(msg1, ...)表示执行该操作.

很好地理解这种差异可以带来更好的程序设计.例如,当保证评估与执行分开时,可以优化,缓存或检测等,而不改变程序的含义.在诸如Haskell之类的语言中,语言的设计保证了评估和执行的分离,编译器获得了前所未有的优化自由,从而产生了更快的二进制代码.

虽然我没有读过你所指的那本书,但我猜这个例子的目的是为了证明这种差异,因此,虽然推迟计算没有实际意义,但可能会有一个教育性的.