聖誕快樂~
今天就來說說Scala的Currying吧,Scala官方說法為:
Methods may define
multiple parameter lists
. When a method is called with afewer number of parameter lists
, then this will yield a function taking themissing parameter
lists as its arguments.
簡單說,currying是將接受多個參數的函式
轉換成一個接受較少參數&回傳剩餘參數的函式
的過程。
這有啥好處勒?可以讓函式重用率提高!!我們還是看看例子吧:
scala> def add(x: Int, y: Int) = x + y
add: (x: Int, y: Int)Int
scala> add(5, 7)
res0: Int = 12
一般定義加法函式的方式,帶入兩個參數,回傳Int,沒啥特別
scala> def addCurrying(x: Int) = (y: Int) => x + y
addCurrying: (x: Int)Int => Int
scala> addCurrying(5)(7)
res1: Int = 12
(x: Int)
,但回傳值則不是普通Int,而是另外一個函式,並且該函式使用另外一個沒用參數(也就是y),注意在此回傳型別為Int=>Int
,代表回傳型別為傳入一個Int,而輸出為Int的函式
。addCurrying(5)(7)
,拆開看,addCurrying(5)會回傳y=5+y的匿名函式
,然後(7)再帶入該函式得到12。scala> def addScalaCurrying(x: Int)(y: Int) = x + y
addScalaCurrying: (x: Int)(y: Int)Int
scala> addScalaCurrying(5)(7)
res2: Int = 12
addScalaCurrying(x: Int)(y: Int)
這種寫法是Scala對Currying提供的語法蜜糖,作用與前一個例子無異
對Currying有了模糊的了解後,那該如何用他勒?currying有啥好處:
再看一些例子吧:
scala> def concatenator(w1: String): String => String = { ①
w2 => w1 + " " + w2
}
concatenator: (w1: String)String => String
scala> val gretting = concatenator("Hi") ②
gretting: String => String = <function1>
scala> gretting("Joe") ③
res2: String = Hi Joe
scala> gretting("Readers") ④
res3: String = Hi Readers
① 定義一個函數concatenator,其輸出也是一個函數。(String=>String的函式型別
② 呼叫一個concatenator("Hi")
,仔細看會回傳(w2 => "Hi" + " " +w2)
的一個匿名函式給greeting
③④對greeting傳入不同參數的效果。
他原本就只有一個參數阿
~只是他的回傳值是另外一個函數罷了。OK,來將concatenator柯里化(大陸用法XD)Currying吧:
scala> def concatenatorCurried(w1: String)(w2: String) = w1 + " " + w2 ①
concatenatorCurried: (w1: String)(w2: String)String
scala> concatenatorCurried("Hi")("Joe") ②
res4: String = Hi Joe
scala> val greetingPrefix = concatenatorCurried("Hi")_ ③
greetingPrefix: String => String = <function1>
scala> greetingPrefix("Joe") ④
res5: String = Hi Joe
① 定義一個Currying的concatenator
② 用看看,感覺沒啥不同
③ 若我確定greeting都會是Hi,只會換名子,那我可以宣告成concatenatorCurried("Hi")_
,_
就類似placeholder的概念。
④對greeting傳入參數的效果。
這感覺跟之Currying時感覺差不多阿,OK來了,如果我想要固定名稱是Joe,只換Greeting的話呢?
concatenatorCurried(_:String)("Joe")
即可scala> val GrettingToJoe = concatenatorCurried(_:String)("Joe")
GrettingToJoe: String => String = <function1>
scala> GrettingToJoe("How are you")
res6: String = How are you Joe
scala> GrettingToJoe("Hi")
res7: String = Hi Joe
有感受到Currying的威力嗎?他可以讓函式更有彈性,架構設計上就會有個currying的generalized function
,然後針對不同情境保留不同placeholder
,看最後一個例子:
scala> def isInRange(from: Int, to: Int)(point: Int) = { ①
if (point >= from && point <= to) true
else false
}
isInRange: (from: Int, to: Int)(point: Int)Boolean
scala> val fromZeroToTen = isInRange(0, 10) _ ②
fromZeroToTen: Int => Boolean = <function1>
scala> fromZeroToTen(5)
res8: Boolean = true
scala> fromZeroToTen(10)
res9: Boolean = true
scala> val isFiveInRange = isInRange(_: Int, _: Int)(5) ③
isFiveInRange: (Int, Int) => Boolean = <function2>
scala> isFiveInRange(0, 10)
res10: Boolean = true
scala> isFiveInRange(50, 100)
res11: Boolean = false
①currying的一般化函式,接收兩個參數(from: Int, to: Int)(point: Int)
②情境1
③情境2
情境1、2 由相同的一般化函式延伸出來,讓一般化函式能更泛用
以後看到一些函式後面跟著_
知道是啥意思了吧~