关于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提到了一些关于表达式的内容,我希望有人可以解释一下这个问题。看起来编译器似乎是智能地预先执行事物来计算值...但我不知道?
答案 0 :(得分:14)
ExeA
和ExeC
不是函数,而是单个值。编译器确保值按照在源文件中声明的顺序初始化,因此这里发生的是:
ExeC
初始化ExeA
初始化Example 1
的初始值打印ExeA
ExeB
函数被称为正常Example 3
的初始值打印ExeC
醇>
如果您希望ExeA
和ExeC
真正变得懒惰 - 也就是说,要控制其副作用的运行时间 - 您可以将它们变成接受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
的主体中简单地声明pieDiv4
和calc
,但是它们不太清楚它们是如何被使用的并且你弄脏了变量空间。另一种选择是将这些子计算分解为一个单独的私有辅助函数。但是我不喜欢这样的功能,因为很多人很难分析你的复杂算法,因为你总是在寻找各种实现部分。此外,它还有很多锅炉板代码和价值传递。这就是为什么F#函数默认是“公共”的,词法作用域和闭包允许您在面向公众的函数中分层组织“私有”函数和值。