Spo*_*ser 416 language-agnostic terminology definition currying partial-application
我经常在互联网上看到各种各样的抱怨,其他人的currying例子并不是currying,但实际上只是部分应用.
我没有找到关于部分应用是什么的合理解释,或者它与currying有何不同.似乎存在普遍的混淆,在某些地方将等效的例子描述为currying,在其他地方描述为部分应用.
有人可以向我提供这两个术语的定义,以及它们如何区别的细节吗?
Mar*_*ade 246
Currying将n个参数的单个函数转换为n个函数,每个函数都有一个参数.鉴于以下功能:
function f(x,y,z) { z(x(y));}
Run Code Online (Sandbox Code Playgroud)
咖喱变成:
function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }
Run Code Online (Sandbox Code Playgroud)
为了获得f(x,y,z)的完整应用,您需要这样做:
f(x)(y)(z);
Run Code Online (Sandbox Code Playgroud)
许多函数式语言都可以让你写f x y z.如果你只调用f x y或f(x)(y)然后你得到一个部分应用的函数 - 返回值是一个闭包,lambda(z){z(x(y))}传入x和y的值为f(x,y).
使用部分应用程序的一种方法是将函数定义为广义函数的部分应用程序,如fold:
function fold(combineFunction, accumulator, list) {/* ... */}
function sum = curry(fold)(lambda(accum,e){e+accum}))(0);
function length = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);
/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list) //returns 10
Run Code Online (Sandbox Code Playgroud)
Pac*_*ier 155
了解它们之间差异的最简单方法是考虑一个真实的例子.假设我们有一个函数Add,它将2个数字作为输入并返回一个数字作为输出,例如Add(7, 5)返回12.在这种情况下:
使用Add值部分应用函数7将为我们提供一个新函数作为输出.该函数本身需要1个数字作为输入并输出一个数字.因此:
Partial(Add, 7); // returns a function f2 as output
// f2 takes 1 number as input and returns a number as output
Run Code Online (Sandbox Code Playgroud)
所以我们可以这样做:
f2 = Partial(Add, 7);
f2(5); // returns 12;
// f2(7)(5) is just a syntactic shortcut
Run Code Online (Sandbox Code Playgroud)哗众取宠的功能Add会给我们一个新的功能输出.该功能本身需要1号作为输入,并输出又另一个新的功能.然后第三个函数将1个数作为输入并返回一个数作为输出.因此:
Curry(Add); // returns a function f2 as output
// f2 takes 1 number as input and returns a function f3 as output
// i.e. f2(number) = f3
// f3 takes 1 number as input and returns a number as output
// i.e. f3(number) = number
Run Code Online (Sandbox Code Playgroud)
所以我们可以这样做:
f2 = Curry(Add);
f3 = f2(7);
f3(5); // returns 12
Run Code Online (Sandbox Code Playgroud)换句话说,"currying"和"partial application"是两个完全不同的功能.Currying只需1个输入,而部分应用需要2个(或更多)输入.
即使它们都返回一个函数作为输出,返回的函数也是完全不同的形式,如上所示.
dod*_*der 49
注意:这是从F#Basics中获取的,这是.NET开发人员进入函数式编程的优秀介绍性文章.
Currying意味着将具有许多参数的函数分解为一系列函数,每个函数都接受一个参数并最终产生与原始函数相同的结果.对于功能编程新手来说,Currying可能是最具挑战性的话题,特别是因为它经常与部分应用混淆.您可以在此示例中看到两者都在工作:
Run Code Online (Sandbox Code Playgroud)let multiply x y = x * y let double = multiply 2 let ten = double 5您应该立即看到与大多数命令式语言不同的行为.第二个语句通过将一个参数传递给一个带两个的函数来创建一个名为double的新函数.结果是一个函数,它接受一个int参数并产生相同的输出,就好像你已经调用了multiply,x等于2,y等于那个参数.在行为方面,它与此代码相同:
Run Code Online (Sandbox Code Playgroud)let double2 z = multiply 2 z通常,人们错误地认为乘法是形成双重的.但这只是有点真实.乘法函数是curry,但是在定义时会发生这种情况,因为F#中的函数默认是curry.当创建双重函数时,更准确地说,部分应用了乘法函数.
乘法函数实际上是一系列两个函数.第一个函数接受一个int参数并返回另一个函数,有效地将x绑定到特定值.此函数还接受一个int参数,您可以将其视为绑定到y的值.在调用第二个函数之后,x和y都被绑定,因此结果是x和y的乘积,如double体中所定义.
要创建double,将计算乘法函数链中的第一个函数以部分应用乘法.结果函数的名称为double.当计算double时,它使用其参数以及部分应用的值来创建结果.
Jon*_*eet 32
有趣的问题.经过一番搜索后,"部分功能应用程序没有进行调整"给出了我发现的最佳解释.我不能说实际差异对我来说特别明显,但后来我不是FP专家......
另一个有用的页面(我承认我尚未完全阅读)是"使用Java闭包进行Currying和部分应用程序".
看起来这看起来像是一对广泛混淆的术语,请注意.
小智 14
我在另一个主题/sf/answers/899280581/中回答了这个问题.简而言之,部分函数应用程序是关于修复给定多变量函数的一些参数以产生具有较少参数的另一个函数,而Currying是关于将N个参数的函数转换为返回一元函数的一元函数... [示例在这篇文章的末尾显示了Currying.]
Currying主要是理论上的兴趣:人们可以仅使用一元函数来表达计算(即每个函数都是一元的).在实践中和作为副产品,如果语言具有curried功能,它是一种可以使许多有用(但不是全部)部分功能应用程序变得微不足道的技术.同样,它不是实现部分应用程序的唯一方法.因此,您可能会遇到以其他方式完成部分应用程序的情况,但人们将其误认为是Currying.
(Currying的例子)
在实践中,人们不会只写
lambda x: lambda y: lambda z: x + y + z
Run Code Online (Sandbox Code Playgroud)
或等效的JavaScript
function (x) { return function (y){ return function (z){ return x + y + z }}}
Run Code Online (Sandbox Code Playgroud)
代替
lambda x, y, z: x + y + z
Run Code Online (Sandbox Code Playgroud)
为了Currying.
Currying是一个参数的函数,它接受一个函数f并返回一个新函数h.请注意,h从中获取参数X并返回映射到的函数:YZ
curry(f) = h
f: (X x Y) -> Z
h: X -> (Y -> Z)
Run Code Online (Sandbox Code Playgroud)
部分应用程序是两个(或多个)参数的函数f,它接受一个函数和一个或多个附加参数f并返回一个新函数g:
part(f, 2) = g
f: (X x Y) -> Z
g: Y -> Z
Run Code Online (Sandbox Code Playgroud)
产生混淆是因为使用双参数函数时,以下等式成立:
partial(f, a) = curry(f)(a)
Run Code Online (Sandbox Code Playgroud)
双方将产生相同的单参数函数.
对于更高的arity函数,相等性不正确,因为在这种情况下currying将返回单参数函数,而部分应用程序将返回多参数函数.
差异也在于行为,而currying递归地转换整个原始函数(每个参数一次),部分应用只是一步替换.
来源:维基百科Currying.
通过以下JavaScript示例可以最好地说明curry和部分应用程序之间的区别:
function f(x, y, z) {
return x + y + z;
}
var partial = f.bind(null, 1);
6 === partial(2, 3);
Run Code Online (Sandbox Code Playgroud)
部分应用导致更小的功能; 在上面的例子中,farity为3,而partial只有2的arity.更重要的是,部分应用的函数会在调用时立即返回结果,而不是currying链中的另一个函数.因此,如果你看到类似的东西partial(2)(3),它实际上并不是部分应用.
进一步阅读:
我在学习时经常遇到这个问题,此后被问过很多次。我可以描述差异的最简单方法是两者是相同的:) 让我解释一下……显然存在差异。
部分应用和柯里化都涉及向函数提供参数,可能不是一次全部。一个相当规范的例子是将两个数字相加。在伪代码中(实际上是没有关键字的JS),基本函数可能如下:
add = (x, y) => x + y
Run Code Online (Sandbox Code Playgroud)
如果我想要一个“addOne”函数,我可以部分应用它或咖喱它:
addOneC = curry(add, 1)
addOneP = partial(add, 1)
Run Code Online (Sandbox Code Playgroud)
现在使用它们很清楚:
addOneC(2) #=> 3
addOneP(2) #=> 3
Run Code Online (Sandbox Code Playgroud)
那么有什么区别呢?嗯,这很微妙,但部分应用程序涉及提供一些参数,然后返回的函数将在下一次调用时执行主函数,而柯里化将继续等待,直到它拥有所有必需的参数:
curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want
partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error
Run Code Online (Sandbox Code Playgroud)
简而言之,使用部分应用来预填充一些值,知道下次调用该方法时,它会执行,留下 undefined 所有未提供的参数;当您想根据需要不断返回部分应用的函数以完成函数签名时,请使用柯里化。最后一个人为的例子:
curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works
partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助!
更新:某些语言或 lib 实现将允许您将 arity(最终评估中的参数总数)传递给部分应用程序实现,这可能会将我的两个描述混为一谈……但在这一点上,这两种技术是很大程度上可以互换。
Curry:让您调用一个函数,将其拆分为多个调用,每个调用提供一个参数。
部分:允许您调用一个函数,将其拆分为多个调用,并为每个调用提供多个参数。
两者都允许您调用提供较少参数的函数(或者更好地,累积提供参数)。实际上,它们两者(在每次调用时)都将特定值绑定到函数的特定参数。
当函数具有两个以上的参数时,可以看到真正的区别。
(用Javascript)
function process(context, success_callback, error_callback, subject) {...}
Run Code Online (Sandbox Code Playgroud)
为什么总是传递参数,例如上下文和回调,如果它们总是相同的呢?只需为函数绑定一些值
processSubject = _.partial(process, my_context, my_success, my_error)
Run Code Online (Sandbox Code Playgroud)
并调用它subject1和foobar的用
processSubject('subject1');
processSubject('foobar');
Run Code Online (Sandbox Code Playgroud)
舒服,不是吗?
使用currying,您每次需要传递一个参数
curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls
result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar');
// same as: process(my_context, my_success, my_error, 'foobar');
Run Code Online (Sandbox Code Playgroud)
我跳过了所有的学术/数学解释。因为我不知道。也许有帮助
这里很多人都没有正确解决这个问题,也没有人谈论重叠。
柯里化(Currying):允许您调用一个函数,将其拆分为多个调用,每次调用提供一个参数。
部分应用程序:允许您调用一个函数,将其拆分为多个调用,并为每个调用提供多个参数。
两者之间的显着差异之一是对部分应用函数的调用立即返回结果,而不是柯里化链中的另一个函数;对于参数数大于 2 的函数,可以清楚地说明这种区别。
这意味着什么?这意味着最多有两次对部分函数的调用。柯里化有多少参数就有多少。如果柯里化函数只有两个参数,那么它本质上与偏函数相同。
部分应用和柯里化
function bothPartialAndCurry(firstArgument) {
return function(secondArgument) {
return firstArgument + secondArgument;
}
}
const partialAndCurry = bothPartialAndCurry(1);
const result = partialAndCurry(2);
Run Code Online (Sandbox Code Playgroud)
部分申请
function partialOnly(firstArgument, secondArgument) {
return function(thirdArgument, fourthArgument, fifthArgument) {
return firstArgument + secondArgument + thirdArgument + fourthArgument + fifthArgument;
}
}
const partial = partialOnly(1, 2);
const result = partial(3, 4, 5);
Run Code Online (Sandbox Code Playgroud)
柯里化
function curryOnly(firstArgument) {
return function(secondArgument) {
return function(thirdArgument) {
return function(fourthArgument ) {
return function(fifthArgument) {
return firstArgument + secondArgument + thirdArgument + fourthArgument + fifthArgument;
}
}
}
}
}
const curryFirst = curryOnly(1);
const currySecond = curryFirst(2);
const curryThird = currySecond(3);
const curryFourth = curryThird(4);
const result = curryFourth(5);
// or...
const result = curryOnly(1)(2)(3)(4)(5);
Run Code Online (Sandbox Code Playgroud)
当我有时间的时候我会写这个,很快。
| 归档时间: |
|
| 查看次数: |
47243 次 |
| 最近记录: |