【问题标题】:F# tail call broken by lack of parenthesesF# 尾调用因缺少括号而中断
【发布时间】:2015-03-07 20:04:58
【问题描述】:

在与 F# 团队 blog article 测试 F# 尾调用时,我发现 几乎相同 代码具有相同的结果,但 不同 IL 虽然代码中只有括号不同。

下一个代码由编译器优化,我在 IL 末尾看到 br.s IL_0000 并且没有调用 sumSoFar

let rec loopAndSum aList sumSoFar = 
    match aList with
    | [] -> sumSoFar
    | x :: xs -> 
        loopAndSum xs (sumSoFar + x)

loopAndSum [ 1..5 ] 0 
|> printfn "sum: %i"

但是该部分没有经过编译器优化,并且在 IL 末尾附近有 call bla_bla.loopAndSum

let rec loopAndSum aList sumSoFar = 
    match aList with
    | [] -> sumSoFar
    | x :: xs -> 
        loopAndSum xs sumSoFar + x

loopAndSum [ 1..5 ] 0 
|> printfn "sum: %i"

这些示例的不同之处仅在于 sumSoFar + x 周围的括号。 您可以使用它并查看 IL .NET Fiddle

有人知道为什么括号很重要吗?

【问题讨论】:

  • 你是否真的测试过他们是否做了同样的事情。第二个版本将始终将 sumSoFar 设置为 0。
  • @JohnPalmer,两个版本都在 LinqPad 和小提琴中本地打印 sum: 15
  • 如果你每次打印sumSoFar你会得到不同的结果。
  • @JohnPalmer,我发现不是。请在小提琴中编写示例。

标签: f# tail-recursion tail-call-optimization tail-call


【解决方案1】:

函数应用的优先级高于任何运算符。所以没有括号,就相当于:

(loopAndSum xs sumSoFar) + x

因此它不是尾调用:加法是在递归调用之后执行的。结果值正确只是偶然。

【讨论】:

  • 感谢您的澄清。你能指出规范的描述吗?
  • this page的底部有一个运算符优先级表,函数应用在**和模式匹配之间的底部附近。
猜你喜欢
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 2015-03-31
  • 2016-08-16
  • 2018-04-10
  • 1970-01-01
相关资源
最近更新 更多