柯里化相当于函数重构;

偏函数相当于函数适配。

So, what is the difference between currying and partial application? As we stated before:

  • Currying: Ability to decompose a function with arity N (where N is > 1) in a chain of calls to smaller functions with arity 1.
  • Partial application: Possibility to apply a function with a given set of arguments to reduce the original function arity. A requirement to do partial application is that the function is already curried so that we can apply arguments one by one.

https://dzone.com/articles/functional-programming-functions

Currying

Currying is the process of taking a function that accepts Narguments and turning it into a chained series of N functions each taking 1 argument.

If we had an add() function that accepted 3 arguments and returned the sum,

we can turn it into a curried function as follows:

How does currying work? It works by nesting functions for each possible argument, using the natural closure created by the nested functions to retain access to each of the successive arguments.

What we want is a way to easily convert an existing function that takes Narguments into its curried version without having to write-out each curried version of a function as we did with curriedAdd().

Let's see if we can decompose this and build something useful.

Writing a generic curry()

Ideally, this is the curry() function interface we'd like to design:

Our curry() returns a new function that allows us to call it with one or more arguments, which it will then partially apply; up until it receives the last argument (based on the original function's arity) at which point it will return the evaluation of invoking the original function with all the arguments.

We know we can access the arity of a function using the .length property of the function. We can use this knowledge to allow us to know how many chained sequences of that function we need to call.

And, we'll need to store the original function passed in as well, so that once we have all the required arguments we can call the original function with the proper arguments and return its results.

Here's our first attempt:

Let's break this down in detail...

  • line 2 our curry function returns a new function, in this case a named function expression called curried().
  • line 3 every time this function is called, we store the arguments passed to it in args
  • line 4 if the number of arguments is equal to the arity of the original function, we have them all, so
  • line 5 return the invocation of the original function with all the arguments
  • line 6 otherwise, return a function that will accept more arguments that, when called, will call our curried function again with the original arguments passed previously combined with the arguments passed to the newly returned function.

Let's give this a try with our original add function from before.

Excellent! This seems to do exactly what we want it to do. However, suppose we had an object with functions that depended on the proper object being set to the calling context (this) of the function. Can we use our curry function to curry an object method?

Uh oh. That's not what we wanted.

Using our curry() function as a method decorator1 seems to break the object context expected by the method. We'll have to retain the original context and be sure and pass it along to successive calls to the returned curried function.

Let's try it again.

Got it! Now our curry() function is context aware and can be used in any number of situations as a function decorator.

Currying Variadic Functions?

So our current solution works and correctly retains the context when called, as long as those functions being curried only accept exactly the number of arguments they declare - no more, no less. This doesn't help us if we want to curry a function that has optional declared arguments or a variable number of arguments (variadic functions).

With variable argument functions, we need a way to tell our curry() function when it has enough arguments to evaluate the original function that it's currying.

Take the following functions

In the above, if we tried to use curry(max)(1)(2)(3) it evaluates too soon and we get a TypeError.

If we try to use curry(range)(1)(10) it never evaluates and simply stops by returning us a function that is still expecting another argument.

There is no feasible implementation of curry which will suffice for the example of our max() function which can take any number of arguments. Without an arity or a minimum number of arguments, there's no efficient way to determine when we should evaluate the original function with the arguments up to that point.

However, we can try to handle the case of optional, trailing arguments as in our range() example; and with minimal changes to our original curry()implementation.

We can modify our original curry() function to take an optional second argument which is the minimum number of arguments to curry.

Now, we can call curry(range, 2)(1)(10) as well as curry(range, 2)(1)(10,2). Unfortunately, though, we can't call it as curry(range, 2)(1)(10)(2), because as soon as it sees the minimum number of arguments, 2 in this case, it will return the results of evaluating the curried function.

What to do about Currying then?

It's clear from our examination above that currying in Javascript is definitely possible and useful. However, because Javascript allows any function to be variadic by nature, it becomes inefficient to implement a curry() function that can handle all possible cases.

Solution: If you're writing in a functional style, with unary and/or binary functions; and those functions take specific arguments that are declared, take advantage of currying. Otherwise, using our original implementation of curry() with the understanding that there are limitations surrounding functions with optional or variable arguments might be the best approach.

Partial Application

That was a lot of talking about currying; so, what is partial application?

Partial application means taking a function and paritallyapplying it to one or more of its arguments, but not all, creating a new function in the process.

Javascript already lets you do this with Function.prototype.bind()

But, that sounds a lot like what our curry() function does internally. If you have curry(), you also have partial application!2

The primary difference is in how you use them. We can implement a partial application function fairly easily (this is applying arguments from left to right)

And we can call this, much like bind but without the initial context argument, like var add6 = apply(add3, 2, 4).

ES6 curry and apply implementations

To wrap things up, I promised to cover the ES6 implementations as well, so here is curry(),

and here's our apply() function in ES6 as well.

The ...(spread/gather) operator and => fat arrow functions simplify the amount of code we need to implement our previous ES5 versions of curry and apply; and, I think they make the implementation more clear and understandable as well.3

You can find more references on this topic from these great posts as well.

https://www.datchley.name/currying-vs-partial-application/

Currying vs Partial Application的更多相关文章

  1. [Functional Programming] From simple implementation to Currying to Partial Application

    Let's say we want to write a most simple implementation 'avg' function: const avg = list => { let ...

  2. 函数式编程之-Partial application

    上一篇关于Currying的介绍,我们提到F#是如何做Currying变换的: let addWithThreeParameters x y z = x + y + z let intermediat ...

  3. JavaScript中的Partial Application和Currying

    这篇文章是一篇学习笔记,记录我在JS学习中的一个知识点及我对它的理解,知识点和技巧本身并不是我原创的.(引用或参考到的文章来源在文末) 先不解释Partial Application(偏函数应用)和C ...

  4. 偏函数应用(Partial Application)和函数柯里化(Currying)

    偏函数应用指的是固化函数的一个或一些参数,从而产生一个新的函数.比如我们有一个记录日志的函数: 1: def log(level, message): 2: print level + ": ...

  5. 函数式编程之-定义能够支持Partial application的函数

    是时候介绍如何在F#中定义函数了,在你没有接触过函数式编程语言之前,你也许会觉得C#/Java的语法已经够丰富了,有什么任务做不了呢?当你读过函数式编程之Currying和函数式编程之Partial ...

  6. [Functional Programming] Create Reusable Functions with Partial Application in JavaScript

    This lesson teaches you how arguments passed to a curried function allow us to store data in closure ...

  7. 函数部分应用Partial application

    def adder(m:Int,n:Int)=m+n val add2 = adder(2,_:Int) println(add2(3)) val add3 = adder(_:Int,3) prin ...

  8. Currying 及应用

    Currying,中文多翻译为柯里化,感觉这个音译还没有达到类似 Humor 之于幽默的传神地步,后面直接使用 Currying. 什么是 Currying Currying 是这么一种机制,它将一个 ...

  9. Coursera课程 Programming Languages, Part A 总结

    Coursera CSE341: Programming Languages 感谢华盛顿大学 Dan Grossman 老师 以及 Coursera . 碎言碎语 这只是 Programming La ...

随机推荐

  1. Maven学习总结(3)——使用Maven构建项目

    Maven学习总结(三)--使用Maven构建项目 maven作为一个高度自动化构建工具,本身提供了构建项目的功能,下面就来体验一下使用maven构建项目的过程. 一.构建Jave项目 1.1.创建J ...

  2. mysql中的sql查询优化

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where 及order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行null 值判断,否则将导致引擎放弃使用索引而进 ...

  3. web开发如何使用高德地图API(四)通过AMap.Marker自定义标点

    说两句: 以下内容除了我自己写的部分,其他部分在高德开放平台都有(可点击外链访问). 我所整理的内容以实际项目为基础希望更有针对性的,更精简. 点击直奔主题. 准备工作: 首先,注册开发者账号,成为高 ...

  4. [BZOJ1031][JSOI2007]字符加密Cipher(后缀数组)

    传送门 算是个模板. 题目说循环,那就再复制一串拼接上. 然后求后缀数组,再搞就可以. 虽然是求后缀,会在后面多一些字符串,然而题目中说的是循环一圈,但是没有影响. ——代码 #include < ...

  5. 巧克力棒&&新年的巧克力棒

    巧克力棒 题目描述 LYK 找到了一根巧克力棒,但是这根巧克力棒太长了,LYK 无法一口吞进去.具体地,这根巧克力棒长为 n,它想将这根巧克力棒折成 n 段长为 1 的巧克力棒,然后慢慢享用.它打算每 ...

  6. FOJ 10月赛题 FOJ2198~2204

    A题. 发现是递推可以解决这道题,a[n]=6*a[n-1]-a[n-2].因为是求和,可以通过一个三维矩阵加速整个计算过程,主要是预处理出2^k时的矩阵,可以通过这道题 #include <i ...

  7. jsp param动作标签

    param 标签以"名字-值"对的形式为其它标签提供附加消息.这个标签与jsp:include.jsp:forward.jsp:plugin标签一起使用. param 动作标签 & ...

  8. 怎样使用ListView实现一个带有网络请求,解析,分页,缓存的公共的List页面来大大的提高工作效率

    在寻常的开发中常常会有非常多列表页面.每做一个列表页就须要创建这个布局文件那个Adapter适配器文件等等一大堆与之相关的附属的不必要的冗余文件. 假设版本号更新迭代比較频繁,如此以往,就会使项目pr ...

  9. jenkins设置

    selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PAT ...

  10. 64位oracle数据库用32位plsql developer无法连接问题(无法载入oci.dll)

    在64位操作系统下安装oracle数据库,新下载了64位数据库(假设是32位数据库安装在64位的操作系统上,无论是client还是server端.都不要去选择C:\Program Files (x86 ...