1. 程式人生 > >深入理解scala的柯里化( currying or curry )以及其用處

深入理解scala的柯里化( currying or curry )以及其用處

百度百科定義:

柯里化(Currying)是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數且返回結果的新函式的技術。

從數學的角度講,這是一個對函式消元求解的過程:

def f(x:Int,y:Int)=x+y

def g(x:Int)=f(x,1)

def z=g(1)

z=2

那麼z也可以寫成這樣:def z=(x:Int)=>(y:Int)=>x+y

例如:

def add(x:Int,y:Int)=x+y

柯里化後:

def add(x:Int)(y:Int)=x+y

實際實現是scala的語法糖,依次呼叫兩個普通函式,第一次呼叫函式(x),第二次呼叫時使用了(x)的返回值。

def add=(x:Int)=>(y:Int)=>x+y

那麼具體怎麼實現柯里化呢?

假設我原始的普通函式 def add(x:Int,y:Int)=x+y

目標函式是Int=>Int=>Int (或者Int=>(Int=>Int))

大概長這樣def add=(x:Int)=>(y:Int)=>x+y

抽象出來是[引數1=>引數2=>function(引數1,引數2)]
柯里化函式:
       
def curry[A,B,C](f: (A, B) => C): A => (B => C) =

  a => b => f(a, b)

curry: [A, B, C](f: (A, B) => C)A => (B => C)

柯里化函式呼叫非柯里化函式add後:

def add(x:Int,y:Int)=x+y

def addCurry=curry(add)

addCurry: Int => (Int => Int)

測試:

addCurry(1)(2)

res10: Int = 3


在scala的隱式轉換中,currying經常被用到,以monoid為例:

trait Monoid[A] {
     def mappend(a1: A, a2: A): A
      def mzero: A
     }


object IntMonoid extends Monoid[Int] {
     def mappend(a: Int, b: Int): Int = a + b
      def mzero: Int = 0
      }


def sum[A](xs: List[A])(implicit m: Monoid[A]): A = xs.foldLeft(m.mzero)(m.mappend)


implicit val intMonoid = IntMonoid


sum(List(1, 2, 3, 4))


另外通過currying可以更隨意組裝函式:

def combine(a:Int)(b:(Int,Int)=>Int)=(x:Int)=>b(a,x)

combine: (a: Int)(b: (Int, Int) => Int)Int => Int


def add(x:Int,y:Int)=x+y


def minus(x:Int,y:Int)=x-y

結果如下:

combine(1)(add)(1)

res20: Int = 2


 combine(5)(minus)(2)

res21: Int = 3


已經知道curry,那麼逆向函式uncurry呢?

def curry[A,B,C](f: (A, B) => C): A => (B => C)=a=>b=>f(a,b)

我們的目標是把A=>(B=>C)轉為(A,B)=>C,即:

 def uncurry[A,B,C](f: A => B => C): (A, B) => C=(a,b)=>f(a)(b)

測試一下:

def add(x:Int,y:Int)=x+y


curry(add)(1)(2)

res1: Int = 3


uncurry(curry(add))(1,2)

res2: Int = 3