了解懒惰评估

Tom*_*age 3 haskell lazy-evaluation

我试图了解Haskell中懒惰评估方面的问题.

如果我有一个函数调用,如:

negate $ 5 * sqrt 16
Run Code Online (Sandbox Code Playgroud)

我的理解是Haskell将处理第sqrt 16一个,创建一个thunk,允许在需要时计算值.

但是sqrt 16当它被传递给乘法时或者只有当它以某种方式输出到控制台时被评估?

换句话说,当输入GHCi时,表达式的每个部分将按什么顺序进行评估(例如)?

dfl*_*str 8

默认情况下,每个函数和构造函数调用都会成为一个thunk.所以,在这种情况下,评估发生如下:

evaluate "negate $ 5 * sqrt 16" -> <thunk> $ <thunk>
 evaluate "negate"               -> <func>
 evaluate "5 * sqrt 16"          -> <thunk> * <thunk>
  evaluate "5"                    -> 5.0
  evaluate "sqrt 16"              -> 4.0
Run Code Online (Sandbox Code Playgroud)

<thunk>意味着什么是thunk,<func>意味着它是一个不能表示为字符串的函数值.

缩进意味着"父级"将在评估"子级"之前对其进行评估.

因此,如果您编写print (negate $ 5 * sqrt 16),运行时将执行以下步骤:

eval thunk:
  <thunk 1> $ <thunk 2>
eval thunk 1:
  <func> $ <thunk 2>
eval thunk 2:
  <func> $ <thunk 3> * <thunk 4>
(cheating a little here, because (*) is strict, so these three are actually
 one step:)
eval thunk 3:
  <func> $ 5 * <thunk 4>
eval thunk 4 by applying sqrt:
  <func> $ 5 * 4
apply (*):
  <func> $ 20
apply ($):
  -20
Run Code Online (Sandbox Code Playgroud)


ham*_*mar 6

你可以把它想象成外向内,即首先negate被称为.然后它将强制评估其论点,这可能会迫使评估其他表达式等等.您可以使用Debug.Trace.trace,打印其第一个参数,同时在评估时返回第二个参数,以查看GHCi中发生的事件的确切顺序:

> trace "A" (negate (trace "B" (5 * (trace "C" (sqrt 16)))))
A
B
C
-20.0
Run Code Online (Sandbox Code Playgroud)

但是,请注意,允许编译器执行可能更改表达式求值顺序的优化,这就是我们IO在订单重要时使用monad的原因.