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 = 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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM