almost 2 years ago

我們在前篇如何在Scala中實現合成函數中實現下方的函數,
$$f(1,10,g(x))=\sum_{x=1}^{10}g(x),\ where\ g(x)=x\ or\ x^2$$
程式碼如下,

def f(a:Int,b:Int,g:Int=>Int):Int=
{
    if (a>b) 0
    else  f(a+1,b,g)+g(a)
}
val fc =f(1,10,_:Int=>Int)

值得一提的是在這裡fc被稱為partially function
而partially function及currying常常放在一起討論,
主要原因在於兩者常能結合一起使用,使代碼更簡潔優美.
本篇文章分為三個部份

  • 1. 什麼是currying
  • 2. Scala中的currying
  • 3. currying & partially function

什麼是Currying

在Wiki是這樣描述的"currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument."
換句話說,currying主要是將多個參數的函數轉換為只能接收一個單一參數的函數,舉列來說,

def sumOfSquares(x:Int,y:Int):Int=(Math.pow(x,2)+Math.pow(y,2)).toInt

p.s. 這裡使sumOfSquares回傳類型Int其實是沒有必要的,但我們最後會為各位示範currying + partially function,就讓我這麼做吧!!
sumOfSquares(2,3)轉換為sumOfSquares(2)(3)這樣的過程即稱為currying
反之則稱為uncurrying

Scala中的Currying

在Scala中創建currying函數有兩種方式,
第一種以def f(arg1:Type)(agrg2:Type)宣告即可。 如下

def sumOfSquares_Currying(x:Int)(y:Int):Int=(Math.pow(x,2)+Math.pow(y,2)).toInt
sumOfSquares_Currying(2)(3)

另一種創建方式主要是針對已存在的函數做Currying, 如以下代碼

val ss:(Int, Int) => Int=sumOfSquares_Currying(_:Int)(_:Int)
val ss_curried=ss.curried

相當簡單吧!!我們現在已經知道如何在Scala中創建currying函數,它該如何與我們最一開始使到partially function結合呢??

Currying & Partially

在Currying中使用Partially

若想在currying函數中使用partially,就像對一般函數一樣,在還不想給定參數的位置使用_即可, 但不同的是一般函數使用partially必須宣告_`的型別否則會出錯,而在currying函數中並不需要宣告,如下

sumOfSquares_Currying(2)(_) //OK
sumOfSquares_Currying(2)(_:Int) //OK

sumOfSquares(2,_)
//error message: missing parameter type for expanded function ((x$1) => sumOfSquares(2, x$1)) sumOfSquares(2,_)
sumOfSquares(2,_:Int) //OK

在Partially中使用Currying

試著思考一下,我們該如何利用Currying想實現以下的函數h
$$g(x,y)=x^2+y^2$$
$$f(1,10,g(x,2))=\sum_{x=1}^{10}g(x,2)$$
函數f(1,10,g(x,2))即為文章一開始提到的partially function val fc =f(1,10,_:Int=>Int); 函數g即為上述的sumOfSquares_Currying,現在讓我們來看看,如何一行指令結合fcsumOfSquares_Currying實作出上述等式

fc(sumOfSquares_Currying(2))

由於fc的型別為(Int => Int) => Int, 而sumOfSquares_Currying(2)則為Int => Int,因此我們將sumOfSquares_Currying(2)作為fc的參數實現上述的等式。

總結

在這篇文章中,我們提到了currying & partially function,並討論如何將它們結合起來一起使用,大家可以慢慢開始體會到Scala有趣(?)的地方了!!
Scala靈活的語法是它的特色之一,如果你對Scala有什麼好的想法,歡迎留言,我們下次見!!

參考文獻

如何在Scala中實現合成函數
Currying - Wikipedia
The Neophyte's Guide to Scala Part 11: Currying and Partially Applied Functions

← 如何在Scala中實現合成函數 Pattern Matching in Scala (1) →