在Scala中函數和方法有什麼區別

方法能夠做爲一個表達式的一部分出現(調用函數並傳參),可是方法(帶參方法)不能做爲最終的表達式,express

可是函數能夠做爲最終的表達式出現:緩存

scala> //定義一個方法app

scala> def m(x:Int) = 2*xdom

m: (x: Int)Int函數

scala> //定義一個函數this

scala> val f = (x:Int) => 2*xscala

f: Int => Int = <function1>it

scala> //方法不能做爲最終表達式出現io

scala> mconsole

<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 = scala.util.Random@d4c330b

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)

相關文章
相關標籤/搜索