1. 程式人生 > >Scala中方法和函式的區別

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 for
method 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)