在Racket中解释Lambda

12

我正在尝试理解Racket中的lambda用法,但仍不清楚。我知道它们是未命名(匿名)函数,但这有什么好处呢?我需要从其他函数中访问我的函数,那么我该如何调用它们?请解释一下下面的小程序,以及为什么使用lambda更好?谢谢。

; why is this better than below???
(define test
  (lambda (x)
    (lambda (y)
      (+ x y))))

(define add27
  (test 27))

; what's wrong with this???
(define (addTest x)
  (+ x 27))

> (add27 2)
29
> (addTest 2)
29
1个回答

18

在 Racket(和其他函数式编程语言)中,lambda非常有用,当你想要将一个内联、一次性的函数作为参数传递而不必事先定义它时。例如,假设我们想要将一个数字列表平方。我们可以走捷径,首先定义一个 square 函数,然后使用 map

(define (square x)
  (* x x))

(map square '(1 2 3 4 5))
=> '(1 4 9 16 25)

...或者我们可以像这样简单地传递一个lambda

(map (lambda (x) (* x x))
     '(1 2 3 4 5))

=> '(1 4 9 16 25)

正如您所看到的,存在一些情况我们不需要引用函数的名称。当然,如果由lambda表示的过程将在多个部分中被重复使用,或者它是递归的,则给它一个名称是有意义的(这样它就不再是匿名的):

(define square
  (lambda (x) (* x x)))

以上等同于开头第一种定义square的方式。事实上,第一种定义只是为了定义一个函数而已,但最终所有的函数都是lambda表达式

现在让我们看一下你的示例。这里我们使用的是lambda的略微不同的用法,并且也举例说明了它们为什么有用——我们不仅定义了一个函数,还返回了一个函数:

(define test
  (lambda (x)
    (lambda (y)
      (+ x y))))

如果我们这样写可能会更加清晰(由于上述原因,它是等效的):

(define (test x)
  (lambda (y)
    (+ x y)))

甚至更简短 - 在Racket中,我们也可以使用这种语法来实现相同的目的:

(define ((test x) y)
  (+ x y))

并不是说这是一种更好的(或者更差的)定义函数的方式 - 这是一种不同的方法!我们正在定义一个名为test的过程,它接收参数x并返回一个新的匿名函数作为结果,该匿名函数反过来将接收参数y。现在,在这些行中:

(define add27
  (test 27))

我们使用一个 x 值为 27 调用了 test 函数,该函数返回一个匿名函数,我们将其命名为 add27。还记得作为参数接收的 ylambda 吗?现在这个 lambda 已经被命名为 add27,这是柯里化的一个例子。想一想:test 是用于生成将固定值 x 加到给定参数 y 的函数,这就解释了为什么它可以工作:

(add27 2)
=> 29

另一方面,这个函数将始终向其参数添加27,没有办法更改它:

(define (addTest x)
  (+ x 27))

(addTest 2)
=> 29

你看到区别了吗?test可以让我们生成新的函数,添加任意值,而addTest始终添加一个固定值27。如果你想添加例如100,使用test就很简单:

(define add100 (test 100))

但是addTest不能被改变,我们需要编写一个新的函数:

(define (addTest100 x)
  (+ x 100))

我希望这能澄清事情,如有其他关于我的回答的问题,请在评论中随意提问。


1
非常感谢您的解释。我仍在努力理解它的工作原理,但这是一个很好的开始。现在我只需要重读500遍。=) - 1Raptor007
对不起,我仍然不清楚这如何返回一个函数。我不明白。 - 1Raptor007
1
@1Raptor007 一个函数返回最后一个值。例如,当调用 (define (f) 1) 时,它将返回 1。而且,由于 lambda 也是像其他任何值一样的值,如果我们写成 (define (f) (lambda (x) x)),那么我们就有了一个函数,当被调用时,将返回一个匿名函数作为结果。 - Óscar López
1
是的,我现在明白了。我的困惑部分是所有元素的对齐方式。函数中的间距似乎比应该更令人困惑...例如,为什么不能像您在注释中所做的那样编写,而不是在多行缩进上编写。我按照自己理解有意义的间距编写了一些函数,这使得参数和返回值更清晰。我想总有一天我会遵循其他格式。无论如何,非常感谢您,奥斯卡! - 1Raptor007

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