currying 是函数式语言中常常遇到的一个概念,翻译成 柯里化,不是库里化。app
currying 指的是将接收多个参数的函数变换成接收一个单一参数,而且返回接收余下的参数并且返回结果的新函数的技术。函数
提及来比较拗口,直接看下面的代码。scala
def add(x: Int, y: Int): Int = x + y //call add add(1, 2) add(3, 4)
可是若是咱们使用 currying 的写法,那么能够将两个参数分开,接收第一个参数(x),而后返回一个函数,这个函数以第二个参数(y)为参数,返回结果:翻译
def add(x:Int) => (y: Int) = x + y
而后咱们就能够这样调用 add 了。code
add(1)(2) add(3)(4)
scala 对于这种写法提供了语法糖,add 的 currying 写法咱们能够更简单地写为:blog
def add(x: Int)(y: Int): Int = x + y
尝试将一个函数(这里指f)写成 curry 方式:get
def curry[A, B, C](f: (A, B) => C): A => (B => C) = a => b => f(a, b)
这里的 currying 函数,它接收两个参数,分别为 A 类型和 B 类型,而后返回一个 C 类型的结果。那么咱们能够经过获得一个偏函数将其转化为 curry 的方式,即 A => (B => C)。具体流程是,用第一个参数 a ,获得一个函数,这个函数使用 b 做为参数,而后获得结果。这里中间的这个函数,是一个偏函数。
一样咱们也能够写一个 uncurry 的方法(即将 currying 的函数f转化成非 currying 的):it
def uncurry[A, B, C](f: A => B => C): (A, B) => C = (a, b) => f(a)(b)
这里在 currying 的过程当中,使用了偏函数(partial applied function),偏函数是什么?这个其实更好理解一些,通俗的讲,能够理解为定制化函数,好比咱们有一个函数须要两个参数,那么若是咱们返回固定其中一个参数的函数,那么咱们就获得了一个偏函数:io
def add(x: Int, y: Int): Int = x + y def add1(y: Int): Int = add(1, y)
这里咱们就是固定了 add 函数中的 x 参数,而后返回了另一个函数 add1,如今 add1 只有一个参数,咱们直接能够调用 add1:function
add1(5) //6
能够看到,currying 和偏函数的主要区别就是,是否固定了参数,偏函数一旦固定参数以后,就返回了新的函数。而 currying 通常并无固定参数,只是相似于使用了黑魔法,让多个参数的函数,最后变成了一个函数链。
其实在 C 语言中也有偏函数这种用法:
int foo(int a, int b, int c) { return a + b + c; } int foo23(int a, int c) { return foo(a, 23, c); }
可是在函数式语言中,偏函数以及 currying 能发挥更大的做用,缘由是,函数式中,咱们的参数还能够是函数。
def sum(f: Int => Int, a: Int, b: Int): Int = if(a > b) 0 else f(a) + sum(a+1, b)
这里有一个 sum 函数,它将 a 和 b 之间的全部的数,用 f 计算以后相加,好比:
sum(x => x * x, 1, 10)
那么就是计算 1 到 10 之间全部数的平方和。可是若是咱们要计算全部数的立方和呢,四次方呢?
当然咱们能够将 f 传递成不一样的值,可是咱们也能够更简单地直接获得定制化以后的函数,即用不一样的 f 获得不一样的函数。
为了达到咱们的目的,咱们将函数写成 currying 形式:
def sum(f: Int => Int)(a: Int, b: Int): Int = if(a >b) 0 else f(a) + sum(f)(a+1, b)
这个时候 sum 函数的类型是什么呢?是:(Int => Int) => (( Int, Int) => Int)。第一层函数是一个参数类型为 Int => Int 的函数,其返回是一个函数,这个函数以 (Int, Int) => Int 类型为返回值。所以,若是咱们传递不一样的第一层函数的参数,即 Int => Int,那么就将获得不一样的第二层函数,即类型为 (Int, Int) => Int 的函数。
所以,咱们能够传入不一样的 f,传入 f 以后,获得的函数就是以 a 和 b 为参数的函数:
为此咱们将 sumT 函数定义成以下形式,使得 sumT 函数返回一个类型为 (Int, Int) => Int 的函数:
def sumT(f: Int => Int): (Int, Int) => Int = { def sumF(a: Int, b: Int): Int = if(a > b) 0 else f(a) + sumF(a+1, b) sumF }
而后咱们就能够传递不一样的 f,获取不一样的偏函数:
def sum3 = sum(x => x * x * x) //三次方 def sum4 = sum(x => x * x * x * x) //四次方
这里能够看出,偏函数和 currying 是有很大的关联的。函数 currying 后,很容易实现某些偏函数。
在 scala 中,若是须要减小函数的参数,即固定函数的某些参数,那么使用偏函数便可。那么,currying的做用是什么?
做用有二,一个是能够将函数的参数写成{}形式,这对于参数是函数时可读性更好。
在currying以前,可能须要这样写:
using (new File(name), f => {......})
在有了 currying 以后,咱们能够写成更易读的方式:
using(new File(name)){ f => ......}
第二个做用就是用于类型推导:
通常的 currying 能够写成
def f(arg1)(arg2)......(argn) => E //等同于 def f(arg1)(arg2)......(argn-1) => { def g(argn) => E; g} /.更简单点 def f(arg1)(arg2)......(argn-1) => (argsn => E) //继续最后就能够写成 def f = args1 => args2......=>argsn => E
这样写以后,scala 能够对参数之间的类型进行推导:左边参数的类型能够用于推导右边参数的类型,从而某些参数就能够简化写法:
def dropWhile[A](as: List[A], f: A => Boolean): List[A] = ...... //调用 dropWhile(List(1,2,3,4), (x: Int => x < 4)) //currying以后 dropWhile[A](as:List[A])(f: A => Boolean): List[A] = ...... //调用 dropWhile(List(1,2,3,4))(x => x < 4) //这里再也不须要指明 x 的类型
这样就能够用来结合匿名函数的下划线写出更加简洁的代码。
currying 也能够在某些只容许使用单个参数的函数的情景下发挥做用,利用 currying 使用层层嵌套的单参数函数,能够实现语法层面的多参数函数。
[1]: http://stackoverflow.com/questions/8063325/usefulness-as-in-practical-applications-of-currying-v-s-partial-application-i
[2]: http://www.vaikan.com/currying-partial-application/
[3]: http://www.codecommit.com/blog/scala/function-currying-in-scala
[4]: https://book.douban.com/subject/26772149/
[5]: https://www.coursera.org/learn/progfun1/lecture/fOuQ9/lecture-2-2-currying