这个 let 表达式是如何工作的?
How does this let expression work?
鉴于这行 Haskell 代码,我的任务是将其评估为最简单的形式。
let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
我已经得到了答案(当然我自己在 GHCI 中评估过):42
但是,我想在这里更好地了解评估的实际运作方式。总的来说,我想我知道如何(简单地)让表达式工作:
例子
a = let y = 5 in y * 5 -- a == 25
这计算为 25
因为我们将 y
绑定到 5
的值并且 a
被分配给 y*5
的值(部分in
之后)。绑定 y = 5
仅在 let
.
范围内有效
到目前为止,唯一的解释(至少评估为 42)如下:
let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
g
是 (\x -> k (h x))
h
是 (+1)
(函数 (\x -> x+1)
)
k
是 (\x -> x+x)
20
是 g
的输入,它产生 k (h 20)
h 20
给出 20 + 1
= 21
k (h 20)
= k 21
= 21 + 21
= 42
但是令我困惑的是在 let 之后使用 g h k
。这是什么意思?
g h k = ...
是一个函数定义。这意味着将 g
应用于两个参数(名为 h
和 k
)的结果将评估为 ...
部分。换句话说,它是 g = \h -> \k -> ...
.
的快捷方式
所以我们可以将表达式一步步简化为:
let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
let g = \h -> \k -> (\x -> k (h x)) in g (+1) (\x -> x+x) 20
(\h -> \k -> (\x -> k (h x))) (+1) (\x -> x+x) 20
(\k -> (\x -> k ((+1) x))) (\x -> x+x) 20
(\x -> (\x -> x+x) ((+1) x)) 20
(\x -> x+x) ((+1) 20)
(\x -> x+x) 21
21 + 21
42
想想函数定义。如果你写:
g h k x = k (h x)
那么就是一个带三个参数的函数h
、k
和x
以及returnsk (h x)
。这相当于:
g h k = \x -> k (h x)
或:
g h = \k x -> k (h x)
或:
g = \h k x -> k (h x)
所以我们可以在函数头部和主体中的 lambda 表达式之间传递变量。事实上 Haskell 编译器会重写它。
因此,对于 let
表达式,我们定义了一个局部范围的函数,就像上面定义的那样。现在如果我们调用 g (+1) (\x -> x+x) 20
,那么将调用 g
和 h = (+1)
、k = (\x -> x+x)
和 x = 20
。
所以我们将其评价为:
(\x -> x+x) ((+1) 20)
计算结果为:
(\x -> x+x) ((+1) 20)
-> ((+1) 20)+((+1) 20)
-> 21 + 21
-> 42
鉴于这行 Haskell 代码,我的任务是将其评估为最简单的形式。
let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
我已经得到了答案(当然我自己在 GHCI 中评估过):42
但是,我想在这里更好地了解评估的实际运作方式。总的来说,我想我知道如何(简单地)让表达式工作:
例子
a = let y = 5 in y * 5 -- a == 25
这计算为 25
因为我们将 y
绑定到 5
的值并且 a
被分配给 y*5
的值(部分in
之后)。绑定 y = 5
仅在 let
.
到目前为止,唯一的解释(至少评估为 42)如下:
let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
g
是(\x -> k (h x))
h
是(+1)
(函数(\x -> x+1)
)k
是(\x -> x+x)
20
是g
的输入,它产生k (h 20)
h 20
给出20 + 1
=21
k (h 20)
=k 21
=21 + 21
=42
但是令我困惑的是在 let 之后使用 g h k
。这是什么意思?
g h k = ...
是一个函数定义。这意味着将 g
应用于两个参数(名为 h
和 k
)的结果将评估为 ...
部分。换句话说,它是 g = \h -> \k -> ...
.
所以我们可以将表达式一步步简化为:
let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
let g = \h -> \k -> (\x -> k (h x)) in g (+1) (\x -> x+x) 20
(\h -> \k -> (\x -> k (h x))) (+1) (\x -> x+x) 20
(\k -> (\x -> k ((+1) x))) (\x -> x+x) 20
(\x -> (\x -> x+x) ((+1) x)) 20
(\x -> x+x) ((+1) 20)
(\x -> x+x) 21
21 + 21
42
想想函数定义。如果你写:
g h k x = k (h x)
那么就是一个带三个参数的函数h
、k
和x
以及returnsk (h x)
。这相当于:
g h k = \x -> k (h x)
或:
g h = \k x -> k (h x)
或:
g = \h k x -> k (h x)
所以我们可以在函数头部和主体中的 lambda 表达式之间传递变量。事实上 Haskell 编译器会重写它。
因此,对于 let
表达式,我们定义了一个局部范围的函数,就像上面定义的那样。现在如果我们调用 g (+1) (\x -> x+x) 20
,那么将调用 g
和 h = (+1)
、k = (\x -> x+x)
和 x = 20
。
所以我们将其评价为:
(\x -> x+x) ((+1) 20)
计算结果为:
(\x -> x+x) ((+1) 20)
-> ((+1) 20)+((+1) 20)
-> 21 + 21
-> 42