1. 程式人生 > >scala成長之路(6)函數入門

scala成長之路(6)函數入門

int 類型 amp 轉換 包含 使用 def func 表達

眾所周知,scala作為一門極客型的函數式編程語言,支持的特性包括:

  • 函數擁有“一等公民”身份;
  • 支持匿名函數(函數字面量)
  • 支持高階函數
  • 支持閉包
  • 部分應用函數
  • 柯裏化

首先需要指出,在scala中有方法函數對象兩種形態,方法即是通過def關鍵字定義的函數,而函數對象則是通過將方法轉換而來,或lambda賦值而來。

1. 從“一等公民”說起

很多稍微了解過函數式編程的人可能都聽說過“一等公民”這種說法,但卻很少有人能明明白白地說出究竟什麽是“一等公民”。這裏我做個類比你馬上就明白了:現實中,什麽樣的人能被當做一等公民?首先,他必須是個獨立的個體——依賴父母或朋友才能生存的人肯定不能被當做公民,更不用說一等了;其次,這個人必須擁有足夠的自由

——既能上九天攬月,又可下五洋捉鱉,方才能是一等公民。對應到我們的函數式編程,我們可以總結出幾個點:

(1) 函數的定義和調用不依賴其他結構,例如C、python、js、scala,而反面典型就是java,因為java的任何函數(方法)都必須定義在類、接口、枚舉(其實也是類)中,而且任何的方法調用都要通過對象、類的靜態方法或接口(jdk 1.8),方法不可能直接調用,必須依附於其他結構而存在。所以這種情況下函數肯定不是“一等公民”。

(2)函數可以作為函數的參數、返回值,並可以對函數進行變量賦值,而且函數的定義位置極度自由,任何代碼塊裏又能定義函數。

現在我們再來看scala,它完美地契合上邊所有的需求(但是註意,除了腳本形式的scala之外,其他的scala程序也只能包含在class或object中),scala中函數支持在函數內部定義,而且使用lambda表達式定義的函數可以賦值給任何變量、常量,所有函數均可作為返回值、參數。

2. lambda表達式的學問

很多scala初學者都倒在了scala的lambda上,因為scala lambda的靈活多樣,導致很多時候你可能都看不懂。下面我們從最基本的講起:

最基本的:

val fun = (a:Int) => {a < 100 && a > 0}

當r定義的參數為函數時:

def fun1(f:String => Unit) = f("wangyalou")

我們可以方便地使用lambda傳入需要的函數:

fun1((s:String)=>{println(s)})

註意了,一般人都不這麽寫,因為作為參數的lambda可~以~簡~寫~~~~~準備好我要開始啰!首先,省略掉可以推斷出來的類型參數:

fun1((s)=>{println(s)})

當只有一個參數時,=>前的()可省:

fun1(s=>{println(s)})

還可再簡化,scala中可以用_代替只出現一次的參數:

fun1(println(_))  或 fun1(println _)

最後,我們甚至連下劃線都可以不要了:

fun1(println)

註意,最後的情況我們是利用了編譯器支持lambda的“eta轉換”,即在表達式只有一個參數,且整個執行部分就是一個函數調用時,可以直接寫函數名

插一句:eta擴展(eta-expression)是另一個東西,指的是將一個普通方法轉換為函數對象的過程:

val b= too(_,_,_)
val b= too _    //這也可以?是的
val b : (Int,Int,Int) =>Int = foo

其中too為一個參數為3個Int的方法

但是,too(_,_,1)一定不是eta擴展!

下劃線的用法博大精深,這裏再給出一些例子:

例1:lambda作為參數,_可代替只用一次的參數,且省掉“=>”
val nums = Array(1,2,3,4) nums.filter(_>2)
運行結果: res31: Array[Int]
= Array(3, 4)

例2:lambda作為參數,_可代替只用一次的參數,且省掉“=>”

scala> def foo(f:(Int,Int)=>Int)(a:Int,b:Int) = f(a,b)
foo: (f: (Int, Int) => Int)(a: Int, b: Int)Int

scala> foo(_+_)(3,4)
res33: Int = 7

例3:lambda作為函數定義,_可代替只用一次的參數,且省掉“=>”,但這時要加上類型,因為這裏無法推斷出“_”的類型

val b = (_:Int) + (_:Int)

b(1,2)

運行結果:

res32: Int = 3

3. 部分應用函數(偏函數)

一個例子足以說清楚:

scala> def foo(a:Int, b:Int, c:Boolean) = if(c) a+b else a-b
foo: (a: Int, b: Int, c: Boolean)Int
scala
> val foom = foo(_:Int,_:Int,false) foom: (Int, Int) => Int = <function2>

類似於python中的偏函數,這裏將某個參數確定,其他參數用"_"代替並指明其類型,註意一定要指明類型啊!!!不然就成了eta擴展失敗的案例了!!

scala成長之路(6)函數入門