F#代码执行顺序

时间:2010-07-03 09:06:46

标签: f#

关于F#的另一个noob问题。

如果我有以下代码......

let ExeC =
    printfn "c"
    3

let ExeB b = 
    printfn "b"
    2

let ExeA = 
    printfn "a"
    1

printfn "Example %d " ExeA
printfn "Example %d " (ExeB 1)
printfn "Example %d " ExeC

输出如下......

c
a
Example 1
b
Example 2
Example 3

这里似乎不寻常的是代码执行的顺序。在上一个问题中,Brian提到了一些关于表达式的内容,我希望有人可以解释一下这个问题。看起来编译器似乎是智能地预先执行事物来计算值...但我不知道?

2 个答案:

答案 0 :(得分:14)

ExeAExeC不是函数,而是单个值。编译器确保值按照在源文件中声明的顺序初始化,因此这里发生的是:

  1. ExeC初始化
  2. ExeA初始化
  3. 使用Example 1的初始值打印
  4. ExeA
  5. ExeB函数被称为正常
  6. 使用Example 3的初始值打印
  7. ExeC

    如果您希望ExeAExeC真正变得懒惰 - 也就是说,要控制其副作用的运行时间 - 您可以将它们变成接受unit的函数:< / p>

    let ExeC () =
        printfn "c"
        3
    
    let ExeB b = 
        printfn "b"
        2
    
    let ExeA () = 
        printfn "a"
        1
    
    printfn "Example %d " (ExeA ())
    printfn "Example %d " (ExeB 1)
    printfn "Example %d " (ExeC ())
    

答案 1 :(得分:4)

作为蒂姆回答的后续行动,我想你可能会对你偶然发现的事情有所了解。在您的示例中,ExeC和ExeA利用通过词法作用域和闭包来组织代码的功能样式。让我展示一个更有力的例子。

let calc n =
    //...
    let timesPieDiv4 = 
        let pie = 3.14
        let pieDiv4 = pie/4.
        n * pieDiv4

    //...

这里timesPieDiv4不是一个函数,但它有一个包含一系列子计算的主体,这些子计算没有暴露给calc函数的其余部分。在像C#这样的语言中,你有两种选择,它们都不吸引我。第一个选项是在pie的主体中简单地声明pieDiv4calc,但是它们不太清楚它们是如何被使用的并且你弄脏了变量空间。另一种选择是将这些子计算分解为一个单独的私有辅助函数。但是我不喜欢这样的功能,因为很多人很难分析你的复杂算法,因为你总是在寻找各种实现部分。此外,它还有很多锅炉板代码和价值传递。这就是为什么F#函数默认是“公共”的,词法作用域和闭包允许您在面向公众的函数中分层组织“私有”函数和值。