为什么 haskell seq 函数将第一个参数作为 WHNF 的参数?
Why haskell seq function makes first argument to WHNF?
我认为 seq 很难理解,因为它是 WHNF 的第一个参数。
如果seq把第一个参数变成范式,就容易理解了
使用 WHNF 比使用 NF 有什么好处?
为什么 haskell 委员会选择在 seq 函数中使用 WHNF?
首先,Haskell 的 API 在 WHNF 和 NF 之间没有偏好:它同时提供 seq
和 deepseq
。
所以您的问题可能是为什么 WHNF 存在?简而言之,这是尽可能少的评估。完整的计算如下:
- 未评估(thunk)
- WHNF:已知的最外层构造函数
- 更深入的评估
- NF : 该值已完全计算
然而,Haskell 是惰性的,它会尝试尽可能少地计算以获得最终结果。例如,要计算列表 l
的长度,Haskell 必须知道 l
是 []
还是 _ : t
。然后递归地提出关于 t
的相同问题。因此它只需要最终构造函数[]
之前的构造函数数量:
。根据定义,这是连续的 WHNF,它只提取值的最外层构造函数。
最外层的构造函数是您必须了解的有关值的最少信息,以便实际对其进行操作,例如在 case of
中选择一个分支,就像在上面的 length
中一样。这就是WHNF对NF的兴趣。
请注意,对于像 Int
这样的简单类型,每个值都是其自己的构造函数(2
、-7
、15
、...),因此 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
它。或者你的编译器可能有性能错误,你必须用 seq
或 deepseq
来帮助它。分析器会告诉您代码的哪些部分很慢。
我认为 seq 很难理解,因为它是 WHNF 的第一个参数。
如果seq把第一个参数变成范式,就容易理解了
使用 WHNF 比使用 NF 有什么好处?
为什么 haskell 委员会选择在 seq 函数中使用 WHNF?
首先,Haskell 的 API 在 WHNF 和 NF 之间没有偏好:它同时提供 seq
和 deepseq
。
所以您的问题可能是为什么 WHNF 存在?简而言之,这是尽可能少的评估。完整的计算如下:
- 未评估(thunk)
- WHNF:已知的最外层构造函数
- 更深入的评估
- NF : 该值已完全计算
然而,Haskell 是惰性的,它会尝试尽可能少地计算以获得最终结果。例如,要计算列表 l
的长度,Haskell 必须知道 l
是 []
还是 _ : t
。然后递归地提出关于 t
的相同问题。因此它只需要最终构造函数[]
之前的构造函数数量:
。根据定义,这是连续的 WHNF,它只提取值的最外层构造函数。
最外层的构造函数是您必须了解的有关值的最少信息,以便实际对其进行操作,例如在 case of
中选择一个分支,就像在上面的 length
中一样。这就是WHNF对NF的兴趣。
请注意,对于像 Int
这样的简单类型,每个值都是其自己的构造函数(2
、-7
、15
、...),因此 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
它。或者你的编译器可能有性能错误,你必须用 seq
或 deepseq
来帮助它。分析器会告诉您代码的哪些部分很慢。