Scala中方法和函式的區別
在Scala中函式和方法有什麼區別
方法可以作為一個表示式的一部分出現(呼叫函式並傳參),但是方法(帶參方法)不能作為最終的表示式,
但是函式可以作為最終的表示式出現:
scala> //定義一個方法 scala> def m(x:Int) = 2*x m: (x: Int)Int scala> //定義一個函式 scala> val f = (x:Int) => 2*x f: Int => Int = <function1> scala> //方法不能作為最終表示式出現 scala> m <console>:9: error: missing arguments formethod m; follow this method with `_‘ if you want to treat it as a partially applied function m ^ scala> //函式可以作為最終表示式出現 scala> f res9: Int => Int = <function1>
無參方法可以作為最終表示式出現,其實這屬於方法呼叫,scala規定無參函式的呼叫可以省略括號
(關於方法呼叫我們下面會涉及到)
scala> def m1()=1+2 m1: ()Int scala> m1 res10: Int = 3
引數列表對於方法是可選的,但是對於函式是強制的
方法的可以沒有引數列表,引數列表也可以為空。但是函式必須有引數列表(也可以為空),見下面例子
scala> //方法可以沒有引數列表 scala> def m2 = 100; m2: Int scala> //方法可以有一個空的引數列表 scala> def m3() = 100 m3: ()Int scala> //函式必須有引數列表,否則報錯 scala> var f1 = => 100 <console>:1: error: illegal start of simple expression var f1= => 100 ^ scala> //函式也可以有一個空的引數列表 scala> var f2 = () => 100 f2: () => Int = <function0>
那麼方法為什麼可以沒有引數列表呢,往下看。
方法名意味著方法呼叫,函式名只是代表函式自身
因為方法不能作為最終的表示式存在,所以如果你寫了一個方法的名字並且該方法不帶引數(沒有引數列表或者無參)
該表示式的意思是:呼叫該方法得到最終的表示式。因為函式可以作為最終表示式出現,如果你寫下函式的名字,函式
呼叫並不會發生,該方法自身將作為最終的表示式進行返回,如果要強制呼叫一個函式,你必須在函式名後面寫()
scala> //該方法沒有引數列表 scala> m2 res11: Int = 100 scala> //該方法有一個空的引數列表 scala> m3 res12: Int = 100 scala> //得到函式自身,不會發生函式呼叫 scala> f2 res13: () => Int = <function0> scala> //呼叫函式 scala> f2() res14: Int = 100
為什麼在函數出現的地方我們可以提供一個方法
在scala中很多高階函式,如map(),filter()等,都是要求提供一個函式作為引數。但是為什麼我們可以提供一個方法呢
?就像下面這樣:
scala> val myList = List(3,56,1,4,72) myList: List[Int] = List(3, 56, 1, 4, 72) scala> // map()引數是一個函式 scala> myList.map((x) => 2*x) res15: List[Int] = List(6, 112, 2, 8, 144) scala> //嘗試給map()函提供一個方法作為引數 scala> def m4(x:Int) = 3*x m4: (x: Int)Int scala> //正常執行 scala> myList.map(m4) res17: List[Int] = List(9, 168, 3, 12, 216)
這是因為,如果期望出現函式的地方我們提供了一個方法的話,該方法就會自動被轉換成函式。該行為被稱為ETA expansion。
這樣的話使用函式將會變得簡單很多。你可以按照下面的程式碼驗證該行為:
scala> //期望出現函式的地方,我們可以使用方法 scala> val f3:(Int)=>Int = m4 f3: Int => Int = <function1> scala> //不期望出現函式的地方,方法並不會自動轉換成函式 scala> val v3 = m4 <console>:8: error: missing arguments for method m4; follow this method with `_‘ if you want to treat it as a partially applied function val v3 = m4 ^
利用這種自動轉換,我們可以寫出很簡潔的程式碼,如下面這樣
scala> //10.<被解釋成obj.method,即整形的<的方法,所以該表示式是一個方法,會被解釋成函式 scala> myList.filter(10.<) res18: List[Int] = List(56, 72)
因為在scala中操作符被解釋稱方法
- 字首操作符:op obj 被解釋稱obj.op
- 中綴操作符:obj1 op obj2被解釋稱obj1.op(obj2)
- 字尾操作符:obj op被解釋稱obj.op
你可以寫成10<而不是10.<
scala> myList.filter(10<) warning: there were 1 feature warning(s); re-run with -feature for details res19: List[Int] = List(56, 72)
如何強制把一個方法變成函式
可以在方法名後面加一個下劃線強制變成函式,部分應用函式
scala> val f4 = m4 _ f4: Int => Int = <function1> scala> f4(2) res20: Int = 6
傳名引數是一個方法
傳名引數實質是一個沒有引數列表的方法。正是因此你才可以使用名字呼叫而不用新增()
scala> //使用兩次‘x‘,意味著進行了兩次方法呼叫 scala> def m1(x: => Int)=List(x,x) m1: (x: => Int)List[Int] scala> import util.Random import util.Random scala> val r = new Random() r: scala.util.Random = [email protected] scala> //因為方法被呼叫了兩次,所以兩個值不相等 scala> m1(r.nextInt) res21: List[Int] = List(-1273601135, 2004676878)
如果你在方法體部分快取了傳名引數(函式),那麼你就快取了值(因為x函式被呼叫了一次)
scala> //把傳名引數代表的函式快取起來 scala> def m1(x: => Int) ={val y=x;List(y,y)} m1: (x: => Int)List[Int] scala> m1(r.nextInt) res22: List[Int] = List(-1040711922, -1040711922)
能否在函式體部分引用傳名引數所代表的方法呢,是可以的(快取的是傳名引數所代表的方法)。
scala> def m1(x: => Int)={val y=x _;List(y(),y())} m1: (x: => Int)List[Int] scala> m1(r.nextInt) res23: List[Int] = List(-1982925840, -933815401)