这怎么让表达起作用?

unn*_*olo 10 lambda haskell anonymous-function let expression-evaluation

鉴于这一系列的Haskell代码,我的任务是将其评估为最简单的形式.

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
Run Code Online (Sandbox Code Playgroud)

我已经得到了答案(当然在GHCI中我自己进行了评估): 42

但是,我希望更好地了解评估在这里如何运作.一般来说,我想我知道如何(简单)让表达式工作:

a = let y = 5 in y * 5  -- a == 25
Run Code Online (Sandbox Code Playgroud)

该计算结果为25,因为我们绑定y到的价值5a被分配到的值y*5(后部分in).绑定y = 5仅在范围内有效let.

到目前为止,唯一的解释(至少评估为42)如下:

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
Run Code Online (Sandbox Code Playgroud)
  • g(\x -> k (h x))
  • h(+1)(功能(\x -> x+1))
  • k(\x -> x+x)

    1. 20g收益的输入k (h 20)
    2. h 2020 + 1=21
    3. k (h 20)= k 21= 21 + 21=42

但让我感到困惑的是使用g h klet之后.那是什么意思?

Wil*_*sem 10

想一下函数定义.如果你写:

g h k x = k (h x)
Run Code Online (Sandbox Code Playgroud)

然后,它是一个有三个参数的函数h,kx返回k (h x).这相当于:

g h k = \x -> k (h x)
Run Code Online (Sandbox Code Playgroud)

要么:

g h = \k x -> k (h x)
Run Code Online (Sandbox Code Playgroud)

要么:

g = \h k x -> k (h x)
Run Code Online (Sandbox Code Playgroud)

因此,我们可以在函数的头部和正文中的lambda表达式之间传递变量.实际上,Haskell编译器会重写它.

因此,使用let表达式,我们定义了一个局部范围的函数,就像上面定义的那样.现在,如果我们调用g (+1) (\x -> x+x) 20,然后将因此打电话gh = (+1),k = (\x -> x+x)x = 20.

所以我们将其评估为:

(\x -> x+x) ((+1) 20)
Run Code Online (Sandbox Code Playgroud)

其评估结果为:

   (\x -> x+x) ((+1) 20)
-> ((+1) 20)+((+1) 20)
-> 21 + 21
-> 42
Run Code Online (Sandbox Code Playgroud)

  • @unnicolo这只是语法糖.`fx = x`被置于`f =\x - > x`.所有函数最终都是通过将名称绑定到lambda表达式来定义的,将参数从lambda表达式移动到绑定的左侧只是一个更轻量级的语法. (2认同)

sep*_*p2k 8

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
Run Code Online (Sandbox Code Playgroud)