2016-03-18 82 views
0

雖然我理解部分應用/ curried函數是什麼,但我仍然不完全明白爲什麼我會使用這樣的函數而不是簡單地重載函數。即給定:部分應用/咖喱函數vs重載函數

def add(a: Int, b: Int): Int = a + b 
val addV = (a: Int, b: Int) => a + b 

是什麼請注意,我不是問討好VS部分應用功能,因爲這

def addOne(b: Int): Int = add(1, b) 

def addOnePA = add(1, _:Int) 
// or currying 
val addOneC = addV.curried(1) 

之間的實際差別已經問過我已閱讀答案。我問柯里/部分應用功能VS重載函數

+0

重複其他幾個問題 –

+0

柯里格訴v.s的有用性(在實際應用中)可能的重複。部分應用程序在斯卡拉](http://stackoverflow.com/questions/8063325/usefulness-as-in-practical-applications-of-currying-vs-partial-application-i) –

+0

我不是問關於柯里與部分應用函數,我正在問卷曲/部分應用函數與重載函數 –

回答

2

在你的例子不同的是,重載函數將有硬編碼值1的第一個參數add,即在編譯時設置,而部分應用或咖喱功能意味着動態捕捉它們的參數,即在運行時。否則,在你的特定例子中,因爲你在這兩種情況下都編碼爲1,它幾乎是一回事。

當你將它傳遞給不同的上下文時,你會使用部分應用/ curried函數,它會動態地捕獲/填充參數,直到它完全準備好被評估。在FP中這很重要,因爲很多時候你不會傳遞值,而是傳遞函數。它允許更高的可組合性和代碼重用性。

2

有幾個原因可能導致您偏好部分應用功能。最明顯也許很膚淺的一個是,你不必寫出如addOnePA這樣的中間函數。

List(1, 2, 3, 4) map (_ + 3) // List(4, 5, 6, 7) 

是更好比

def add3(x: Int): Int = x + 3 
List(1, 2, 3, 4) map add3 

即使是匿名函數的方法(即下劃線結束了向外擴展到編譯器)的感覺一點點笨重的比較。

List(1, 2, 3, 4) map (x => x + 3) 

當你真正將函數作爲一流值傳遞時,表面上看,部分應用程序不那麼膚淺,派上用場了。

val fs = List[(Int, Int) => Int](_ + _, _ * _, _/_) 
val on3 = fs map (f => f(_, 3)) // partial application 
val allTogether = on3.foldLeft{identity[Int] _}{_ compose _} 

allTogether(6) // (6/3) * 3 + 3 = 9 

試想一下,如果我沒有告訴你什麼fs功能者。使用命名函數替代部分應用程序的技巧變得更難以使用。對於柯里化來說,柯里裏函數經常可以讓你自然地表達產生其他函數的函數的變換(而不是更高階的函數,最終產生一個非函數值),否則這些函數可能會不那麼清晰。

例如,

def integrate(f: Double => Double, delta: Double = 0.01)(x: Double): Double = { 
    val domain = Range.Double(0.0, x, delta) 
    domain.foldLeft(0.0){case (acc, a) => delta * f(a) + acc  
} 

可以被認爲和你實際學會了微積分的整合,即作爲生產其他功能的功能的改造方式使用。

def square(x: Double): Double = x * x 

// Ignoring issues of numerical stability for the moment... 
// The underscore is really just a wart that Scala requires to bind it to a val 
val cubic = integrate(square) _ 
val quartic = integrate(cubic) _ 
val quintic = integrate(quartic) _ 

// Not *utterly* horrible for a two line numerical integration function 
cubic(1) // 0.32835000000000014 
quartic(1) // 0.0800415 
quintic(1) // 0.015449626499999999 

柯里格還減輕了固定功能性的一些問題。

implicit class LiftedApply[A, B](fOpt: Option[A => B]){ 
    def ap(xOpt: Option[A]): Option[B] = for { 
    f <- fOpt 
    x <- xOpt 
    } yield f(x) 
} 

def not(x: Boolean): Boolean = !x 
def and(x: Boolean)(y: Boolean): Boolean = x && y 
def and3(x: Boolean)(y: Boolean)(z: Boolean): Boolean = x && y && z 

Some(not _) ap Some(false) // true 
Some(and _) ap Some(true) ap Some(true) // true 
Some(and3 _) ap Some(true) ap Some(true) ap Some(true) // true 

通過具有令行禁止的功能,我們已經能夠「提升」功能對Option工作,儘可能多的爭論,因爲我們所需要的。如果我們的邏輯功能尚未被使用,那麼我們將不得不分別執行A => BOption[A] => Option[B],(A, B) => C(Option[A], Option[B]) => Option[C],(A, B, C) => D(Option[A], Option[B], Option[C]) => Option[D]等等我們關心的所有ar們。

當涉及到類型推斷時,Currying也有一些其他的好處,如果你有一個方法的參數implicit和非implicit,那麼它是必需的。

Finally, the answers to this question列出了一些你可能想要的咖喱。