【问题标题】:Why haskell seq function makes first argument to WHNF?为什么haskell seq函数将第一个参数传递给WHNF?
【发布时间】:2017-01-25 10:13:10
【问题描述】:

我认为 seq 很难理解,因为它是 WHNF 的第一个参数。

如果 seq 将第一个参数转换为范式,那就很容易理解了。

使用 WHNF 比 NF 有什么好处?

为什么haskell委员会选择在seq函数中使用WHNF?

【问题讨论】:

  • 使用您建议的版本,seq 在更少的输入上会很有用。例如,seq [1..] 永远不会终止。
  • 我不知道这是否会有所帮助(顺便说一下),但seq 可以评估一个thunk。它不是用来递归评估 thunk 的。如果你想递归地做,你也可以这样做,这就是包deepseq的意义所在。
  • 仅供参考,除了基准测试代码之外,我见过的情况很少,deepseq 实际上是适合这项工作的工具。这是一把非常重的锤子。
  • @dfeuer 并行。这是必不可少的,对于那个单一的用例,我发现自己经常使用它。不过,我确实同意您的想法;除了你所说的基准测试之外,我还没有找到任何用例。

标签: haskell seq


【解决方案1】:

首先,Haskell 的 API 在 WHNF 和 NF 之间没有偏好:它同时提供 seqdeepseq

所以也许你的问题是为什么 WHNF 存在?简而言之,这是尽可能少的评估。完整的计算如下:

  1. 未评估(重击)
  2. WHNF:已知的最外层构造函数
  3. 更深入的评估
  4. NF : 该值已完全计算出来

然而,Haskell 是懒惰的,它试图做尽可能少的计算来得到它的最终结果。例如,要计算列表l 的长度,Haskell 必须知道l[] 还是_ : t。然后递归地提出关于t 的相同问题。因此它只需要在最终构造函数[] 之前的构造函数数量:。根据定义,这是连续的 WHNF,它只提取值的最外层构造函数。

最外层的构造函数是你必须知道的关于一个值的最少信息才能实际使用它,例如在case of 中选择一个分支,就像上面的length 一样。这就是 WHNF 对 NF 的兴趣。

请注意,对于像Int 这样的简单类型,每个值都是其自己的构造函数(2-715、...),因此它们的 WHNF = NF。

最后,您很少关心这一切:编译器的工作是尽可能快地执行您的程序。在对foldl (+) 0 [1..100] 的评估中,如果您试图弄清楚[1..100] 列表何时在NF 中结束,您将完全错误。编译器将折叠转换成这个尾递归循环

let sum ret i =
              if i == 100 then 100 + ret
              else sum (ret+i) (i+1) in
  sum 0 1

这意味着它根本不评估列表。不幸的是,当程序的最终结果产生时,编译器无法知道一个值总是在 NF 中。然后,保持它不被评估是浪费时间和 RAM(形成一个 thunk 是有代价的):更好的deepseq 从一开始就是它。或者您的编译器可能存在性能错误,您必须使用seqdeepseq 帮助它。分析器会告诉您代码的哪些部分运行缓慢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-24
    • 2014-04-17
    • 2019-10-19
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 2017-06-15
    • 1970-01-01
    相关资源
    最近更新 更多