球拍-封闭/咖喱,区别在哪里?

Pro*_*eus 1 scheme closures functional-programming currying racket

所以从我个人的研究来看,闭包/柯里化似乎或多或少是完全相同的东西,这显然不可能是正确的。那么区别在哪里呢?

所以这里有一个 Racket 中的闭包示例:

(define (make-an-adder x)
  (lambda (y)
    (+ y x)))

(define add3 (make-an-adder 3))


(add3 5)
Run Code Online (Sandbox Code Playgroud)

会回馈

8
Run Code Online (Sandbox Code Playgroud)

那么柯里化的区别在哪里呢?因为如果我查找文档和其他示例,它们似乎与我为关闭所展示的完全相同?

提前谢谢大家!

Ale*_*uth 5

所以它们是不同的概念,但都与嵌套的 lambda 相关。

闭包可以由 lambda 创建,该 lambda 引用定义在其自身外部的变量,并且当 lambda 从定义该外部变量的上下文中转义时最为重要。Closure 的工作是确保在 lambda 转义该上下文时保留该变量。

Curried 函数是一个可以在多个步骤或多个不同的函数应用程序中接受其参数的函数。这通常意味着在 lambda 中嵌套了 lambda。

柯里化函数并不总是闭包,尽管它们经常是

大多数有用的柯里化函数需要使用闭包,但如果内部 lambda 忽略外部参数,它们就不是闭包。一个简单的例子:

(define (curried-ignore-first ignored)
  (lambda (y) y))
Run Code Online (Sandbox Code Playgroud)

这不是闭包,因为内部 lambda(lambda (y) y)已经关闭:它不引用自身外部的任何变量。

柯里化函数并不总是需要忽略外部参数......它只需要在返回内部 lambda 之前完成对它们的处理,这样内部 lambda 就不会引用外部参数。一个简单的例子是柯里化choose函数。的“正常”定义choose确实使用了闭包:

(define (choose b)
  (lambda (x y)
    (if b x y))) ; inner lambda refers to `b`, so it needs a closure
Run Code Online (Sandbox Code Playgroud)

但是,如果将if b放在外部 lambda 之外,我们可以避免进行闭包:

(define (choose b)
  (if b
      (lambda (x y) x)   ; not closures, just nested lambdas
      (lambda (x y) y)))
Run Code Online (Sandbox Code Playgroud)

闭包并不总是来自柯里化函数

当内部 lambda 引用外部上下文中的变量并且可能会转义该上下文时,就需要闭包。外部上下文通常是一个函数或一个 lambda,但不一定是。它可以是一个让:

(define closure-with-let
  (let ([outer "outer"])
    (lambda (ignored) outer))) ; closure because it refers to `outer`
Run Code Online (Sandbox Code Playgroud)

这是一个闭包,但不是柯里化的例子。

将 Curried-function-production-a-closure 变成一个没有闭包的闭包

原始问题中的示例是一个生成闭包的柯里化函数

(define (make-an-adder x)
  (lambda (y)
    (+ y x)))
Run Code Online (Sandbox Code Playgroud)

如果你想创建一个仍然是具有相同行为的柯里化函数的版本,但x在某些特殊情况下不需要闭包,你可以在 lambda 之前分支:

(define (make-an-adder x)
  (match x
    [0 identity]
    [1 add1]
    [-1 sub1]
    [2 (lambda (y) (+ y 2))]
    [3 (lambda (y) (+ y 3))]
    [_ (lambda (y) (+ y x))]))
Run Code Online (Sandbox Code Playgroud)

这避免了为x精确整数 -1 到 3 的情况产生闭包,但在 的所有其他情况下仍会产生闭包x。如果您将 的域限制x为一个有限集,您可以将它变成一个不需要闭包的函数,只需枚举所有情况即可。

如果你不想要一个闭包 over x,但你对其他东西的闭包没问题,你可以使用递归和组合来构造一个不 close over 的输出函数x

(define (make-an-adder x)
  (cond [(zero? x) identity]
        [(positive-integer? x)
         (compose add1 (make-an-adder (sub1 x)))]
        [(negative-integer? x)
         (compose sub1 (make-an-adder (add1 x)))]))
Run Code Online (Sandbox Code Playgroud)

请注意,这仍然会产生闭包(因为compose在其参数上创建了闭包),但它产生的函数不会关闭 over x。一旦这个版本make-an-adder产生了它的结果,它就“完成”了处理x,不再需要关闭它。