这个let表达式是如何工作的?

12

鉴于这行Haskell代码,我的任务是将其评估为最简单的形式。

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20

我已经得到了答案(当然,在GHCI中也对其进行了评估):42

但是,我想更好地理解这里实际上是如何进行评估的。一般来说,我认为我知道(简单的)let表达式是如何工作的:

示例

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)

    1. 20g 的输入,得到 k (h 20)
    2. h 20 的结果为 20 + 1 = 21
    3. k (h 20) = k 21 = 21 + 21 = 42

但是让我困惑的是在 let 之后使用了 g h k。这是什么意思?


1
不,gg = \h k x -> k (h x) 左边的变量也是参数。 - Willem Van Onsem
g = flip (.) = (>>>)g h k x = k . h $ x = x & (h >>> k) = h x & k - Will Ness
2个回答

12

想象一个函数定义。如果你写下:

g h k x = k (h x)

那么这是一个接受三个参数 hkx 的函数,返回值为 k (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,那么将会以h = (+1)k = (\x -> x+x)x = 20的参数调用g函数。

因此,我们将按以下方式进行评估:

(\x -> x+x) ((+1) 20)

其评估结果为:

   (\x -> x+x) ((+1) 20)
-> ((+1) 20)+((+1) 20)
-> 21 + 21
-> 42

这种在函数头和lambda表达式之间传递变量的过程有没有专门的术语?如果可能的话,我想通过阅读更多相关资料来更好地理解它。 - unnicolo
3
这只是一种语法糖。f x = x 被转化为 f = \x -> x。所有的函数最终都被定义为将名称绑定到 lambda 表达式上,把参数从 lambda 表达式移动到绑定的左侧只是一种更轻量级的语法形式而已。 - chepner

10

g h k = ...是一个函数定义。它表示将g应用于两个参数(命名为hk)的结果将计算为...部分。换句话说,这是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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接