iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 10
0
Big Data

Spark 2.0 in Scala系列 第 10

[Spark-Day10](Scala番外篇) Currying

聖誕快樂~

今天就來說說Scala的Currying吧,Scala官方說法為:

Methods may define multiple parameter lists. When a method is called with a fewer number of parameter lists, then this will yield a function taking the missing 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
  • currying的加法寫法,這次只需帶入一個參數(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傳入不同參數的效果。

注意,此例中concatenator並沒有進行Currying的動作,因為他原本就只有一個參數阿~只是他的回傳值是另外一個函數罷了。

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

  • 參數寫法改為兩個(para1)(para2)
  • 輸出是一般型別而不是函式了

② 用看看,感覺沒啥不同
③ 若我確定greeting都會是Hi,只會換名子,那我可以宣告成concatenatorCurried("Hi")__就類似placeholder的概念。
④對greeting傳入參數的效果。

這感覺跟之Currying時感覺差不多阿,OK來了,如果我想要固定名稱是Joe,只換Greeting的話呢?

  • 沒有Currying:要改寫concatenator!!!
  • Currying化:調整一下: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 由相同的一般化函式延伸出來,讓一般化函式能更泛用

以後看到一些函式後面跟著_知道是啥意思了吧~


上一篇
[Spark-Day9](core API實戰篇) Pair RDD-2
下一篇
[Spark-Day11](core API實戰篇)聚合函數-1
系列文
Spark 2.0 in Scala30

尚未有邦友留言

立即登入留言