2012-07-21 68 views
5

有人问我部分功能应用和关闭之间有什么关系。 我会说没有任何东西,除非我错过了这一点。 比方说,我在python写,我有一个很简单的功能MySum定义如下:部分应用和关闭

MySum = lambda x, y : x + y; 

现在我固定一个参数,以获得更小的元数函数返回相同的值MySum会返回,如果我用同样的参数被称为(部分应用程序):

MyPartialSum = lambda x : MySum(x, 0); 

我可以做同样的事情,在C

int MySum(int x, int y) { return x + y; } 
int MyPartialSum(int x) { return MySum(x, 0); } 

所以,杜MB的问题是:有什么区别?为什么我需要关闭部分应用程序?这些代码是等价的,我不明白封闭和部分应用程序的界限。

回答

3

部分应用程序是一种技术,您可以使用现有函数及其参数的子集,并生成接受其余参数的新函数。

换句话说,如果您有功能F(a, b),应用a的部分应用程序的功能看起来像B(fn, a)其中F(a, b) = B(F, a)(b)

在你的例子中,你只是创建新的功能,而不是将部分应用程序应用于现有的功能。

下面是在python一个例子:

def curry_first(fn, arg): 
    def foo(*args): 
     return fn(arg, *args) 
    return foo 

这产生通过提供的功能和参数的封闭件。返回的新函数使用新的参数签名来调用第一个函数。关闭很重要 - 它允许fn访问arg。现在你可以做这样的事情:

add = lambda x, y : x + y; 
add2 = curry_first(add, 2) 
add2(4) # returns 6 

我通常听说这个简称为currying

+0

我试图通过阅读关于此事的很多东西来澄清事情:在柯里化和部分应用程序之间存在很多混淆,它们通常互换,但是是不同的东西。 维基百科说:“部分应用程序(或部分功能应用程序)是指将一些参数固定到一个函数的过程,产生另一个较小的函数” – Cancer 2012-07-21 09:14:22

+0

这正是上面发生的情况。据我所知,咖喱只是形式的一种特殊表现。 – Hamish 2012-07-21 09:16:32

+0

是的,我明白了,您的代码可以实现这一点,而且非常清晰。但是......我不是也这么做吗?不同的是,很明显,我没有将函数作为参数传递,但是我也修正了一个参数,并创建了一个函数,它具有相同的功能,并返回相同的值,就像我使用所有参数调用原始函数一样。对不起,我觉得很蠢,我很困惑... – Cancer 2012-07-21 09:25:34

0

对我来说,以这种方式使用partialSum,可以确保您只依赖于一个Function来对数字进行求和(MySum),如果出现问题,这将使调试变得更容易,因为您不必担心关于代码的两个不同部分的逻辑代码。

如果将来你决定改变MySum的逻辑,(比方说,使它返回X + Y + 1),那么你就不必担心MyPartialSum,因为它要求MySum

即使它看起来很愚蠢,用这种方式编写代码只是为了简化函数中的依赖关系的过程。我相信你稍后会在你的学习中注意到这一点。

+0

我明白了。无论如何,无论我使用的语言是否支持闭包,我都可以这样做。在这种情况下,闭包是相当无用的 – Cancer 2012-07-21 09:10:51

+0

虽然这只是“编写代码”。部分应用程序应该返回一个*函数*,而不是结果。 – Hamish 2012-07-21 09:14:57

+0

应该吗?这非常有趣!因此,如果我定义: MyPartialSum = lambda x:lambda x:MySum(x,0) 这将是“真正的”部分应用程序?这对我来说不是很清楚 – Cancer 2012-07-21 09:17:49

1

部分函数应用是关于固定的给定功能的一些参数,以产生具有较少参数另一个功能,如

sum = lambda x, y: x + y 
inc = lambda x: sum(x, 1) 

注意,“INC”是“总和”部分地施加,而不脱离捕获任何上下文(正如你提到的关闭)。

但是,这样的手写(通常是匿名)功能有点乏味。可以使用一个函数工厂,它返回一个内部函数。内部函数可以通过从它的上下文捕获一些变量来参数化,像

# sum = lambda x, y: x + y 
def makePartialSumF(n): 
    def partialSumF(x): 
     return sum(x, n) 
    return partialSumF 

inc = makePartialSumF(1) 
plusTwo = makePartialSumF(2) 

这里工厂makePartialSumF被调用两次。每次调用都会产生一个partialSumF函数(将不同的值捕获为n)。使用闭包使得部分应用程序的实现变得方便。所以你可以说部分申请可以通过关闭来实现。当然,关闭可以做很多其他的事情! (作为一个侧节点,Python没有正确闭合。)

柯里是有关开启的N个参数的函数成它返回一个一元函数...例如一元函数,我们有一个函数,有三个参数和返回值:

sum = lambda x, y, z: x + y + z 

的令行禁止版本

curriedSum = lambda x: lambda y: lambda z: x + y + z 

我敢打赌,你就不会写这样的Python代码。 IMO的动机Currying主要是理论上的兴趣。 (一个仅使用一元函数表示计算的框架:每个函数都是一元的!)实际的副产品是,在函数被curry的语言中,一些部分应用程序(当您从左边“修复”参数时)为curried函数提供参数。 (但并不是所有的部分应用都是这样的,例如:当你将y绑定到一个常量以产生两个变量的函数时,给出f(x,y,z)= x + 2 * y + 3 * z)可以说,Currying是一种技术,它在实践中作为副产品可以使许多有用的部分功能应用程序变得微不足道,但这不是Currying的要点。

1

简而言之,部分应用程序的结果通常作为闭包实现。

1

在一种语言中,闭包不是必需的功能。我正在尝试一种自制语言,lambdatalk,其中lambda不会创建闭包,但会接受部分应用。例如这是怎样的集合[利弊,汽车,CDR]可在方案中定义:

(def cons (lambda (x y) (lambda (m) (m x y)))) 
(def car (lambda (z) (z (lambda (p q) p)))) 
(def cdr (lambda (z) (z (lambda (p q) q)))) 

(car (cons 12 34)) -> 12 
(cdr (cons 12 34)) -> 34 

和在lambdatalk:

{def cons {lambda {:x :y :m} {:m :x :y}}} 
{def car {lambda {:z} {:z {lambda {:x :y} :x}}}} 
{def cdr {lambda {:z} {:z {lambda {:x :y} :y}}}} 

{car {cons 12 34}} -> 12 
{cdr {cons 12 34}} -> 34 

在流程外拉姆达节省x和y中的封闭内部的lambda可以访问给定的m。在lambdatalk中,lambda保存:x和:y并返回一个新的lambda,等待:m。所以,即使闭包(和词法范围)是有用的功能,也没有必要。没有任何自由变量,在任何词汇范围之外,功能都是真正的黑盒子,没有任何副作用,完全独立,遵循真实的功能范式。你不这么认为吗?