2010-03-27 49 views
209

我讀Scala Functions(部分斯卡拉的另一個遊覽)。在這個職位他說:Scala中的方法和功能的區別

方法和功能是不一樣的東西

但他沒有解釋任何事情。他想說什麼?

+1

http://jim-mcbeath.blogspot.com/2009/05/scala-functions-vs-methods.html –

+2

我想你可以從[方法和函數之間的區別是什麼]得到一些東西(http://stackoverflow.com/questions/155609/what-is-the-difference-between-a-method-and-a-function) – jinglining

+0

A後續問題與良好的答案:[函數與斯卡拉方法](http://stackoverflow.com/questions/4839537/functions-vs-methods-in-scala) –

回答

60

方法和函數之間的一個很大的實際區別是return的含義。 return只能從方法返回。例如:

scala> val f =() => { return "test" } 
<console>:4: error: return outside method definition 
     val f =() => { return "test" } 
        ^

在方法定義的函數返回不非本地回報:

scala> def f: String = {     
    | val g =() => { return "test" } 
    | g()        
    | "not this" 
    | } 
f: String 

scala> f 
res4: String = test 

而從本地方法只從方法返回返回。

scala> def f2: String = {   
    | def g(): String = { return "test" } 
    | g() 
    | "is this" 
    | } 
f2: String 

scala> f2 
res5: String = is this 
+7

這是因爲返回被封閉捕獲。 –

+9

哇,我可以看到這導致了一些非常混亂的代碼... – Phob

+4

我想不出一次我想從一個函數返回到非本地範圍。事實上,我可以看到,如果一個函數可以決定它想要進一步備份,那麼這是一個嚴重的安全問題。感覺像longjmp,只是更容易意外地出錯。但我注意到scalac不會讓我從函數中返回。這是否意味着這種憎惡已經從這種語言中產生出來? – root

202

吉姆已經得到了這個幾乎涵蓋在his blog post,但我在這裏張貼的簡報以供參考。

首先,讓我們看看Scala規範告訴我們什麼。第3章(類型)告訴我們有關函數類型(3.2.9)和方法類型(3.3.1)。第4章(基本聲明)說到Value Declaration and Definitions(4.1),變量聲明和定義(4.2)和函數聲明和定義(4.6)。第6章(表達式)說明匿名函數(6.23)和方法值(6.7)。奇怪的是,函數值在3.2.9上被說了一次,而沒有其他地方。

功能類型是(大約)類型的形式(T1,...,TN)=>ü,這是在標準庫性狀FunctionN的簡寫的。 匿名函數方法值具有函數類型,函數類型可用作值,變量和函數聲明和定義的一部分。實際上,它可以是方法類型的一部分。

方法類型非值類型。這意味着有值 - 沒有對象,沒有實例 - 帶有方法類型。如上所述,方法值實際上有一個功能類型。方法類型是def聲明 - 除了它的主體外,關於def的所有內容。

值聲明和定義變量聲明和定義valvar聲明,包括類型和值兩者 - 其可以是分別功能類型匿名函數或方法值。請注意,在JVM上,這些(方法值)是用Java調用「方法」的方式實現的。

函數聲明def聲明,包括類型和。類型部分是方法類型,正文是表達式或塊。這也通過Java調用「方法」的方式在JVM上實現。

最後,匿名函數是的功能類型一個實例(即特徵FunctionN的實例),和方法值是一樣的東西!區別在於通過後綴固定下劃線(m _是對應於「函數聲明」(defm)的方法值或通過稱爲eta-expansion的過程來創建方法值,方法類似於從方法到功能的自動轉換。

這就是規格說明,所以讓我把這個預先的:我們不使用那個術語!它導致所謂的「函數聲明」(它是程序的一部分(第4章 - 基本聲明)和「匿名函數」,它是一個表達式,和「函數鍵入「,這是一種類型 - 一種特質。

下面的術語,由經驗豐富的程序員斯卡拉使用,使得從規範的術語一個變化:而不是說函數聲明,我們說方法。甚至方法聲明。此外,我們注意到價值聲明變量聲明也是實際用途的方法。

因此,鑑於上述術語的變化,下面是對這種區別的實際解釋。

功能是一個對象,它包括FunctionX特徵之一,如Function0Function1Function2等,這可能包括PartialFunction爲好,這實際上延伸Function1

讓我們來看看類型簽名的這些特徵之一:

trait Function2[-T1, -T2, +R] extends AnyRef 

這個特點有一個抽象方法(它有一些具體方法爲好):

def apply(v1: T1, v2: T2): R 

這告訴我們所有關於它的知識。甲函數具有接收的類型T1T2,...,TN Ñ參數的apply方法,並返回R類型的東西。它在接收到的參數上是相反的變體,並且結果是共變的。

該差異意味着Function1[Seq[T], String]Function1[List[T], AnyRef]的子類型。作爲子類型意味着它可以用代替它。人們可以很容易地看到,如果我打電話給f(List(1, 2, 3)),並期望返回AnyRef,則上述兩種類型中的任何一種都可以工作。

現在,什麼是相似度的方法和函數?好吧,如果f是一個功能和m是本地的範圍方法,然後既可以這樣調用:

val o1 = f(List(1, 2, 3)) 
val o2 = m(List(1, 2, 3)) 

這些電話實際上是不同的,因爲第一個只是一個語法糖。斯卡拉擴大它到:

val o1 = f.apply(List(1, 2, 3)) 

當然,這是一個方法調用對象f。函數還具有其他語法糖類:函數文字(其中兩個,實際上)和(T1, T2) => R類型簽名。例如:

val f = (l: List[Int]) => l mkString "" 
val g: (AnyVal) => String = { 
    case i: Int => "Int" 
    case d: Double => "Double" 
    case o => "Other" 
} 

的方法和功能之間的另一個相似的是,前者可以容易地轉換成後者:

val f = m _ 

的Scala將擴大,假設m類型是(List[Int])AnyRef成(Scala 2.7):

val f = new AnyRef with Function1[List[Int], AnyRef] { 
    def apply(x$1: List[Int]) = this.m(x$1) 
} 

在Scala 2.8上,它實際上使用了一個AbstractFunction1 cl屁股,以減少班級人數。

請注意,無法將其他方式轉換 - 從函數轉換爲方法。然而,方法有一個很大的優點(好,兩個 - 它們可以稍微快一些):它們可以接收類型參數。例如,儘管上述f未必能夠指定的List接收(List[Int]中的例子)的類型,m可以將其參數化:

def m[T](l: List[T]): String = l mkString "" 

我認爲這幾乎涵蓋了一切,但我會很高興來補充這可以解答任何可能存在的問題。

+22

這個解釋非常清楚。做得好。 不幸的是,Odersky/Venners/Spoon書籍和Scala規範都有些可以互換地使用「功能」和「方法」兩個詞。 (他們可能會說「功能」,其中「方法」會更清楚,但有時也會發生另一種方式,例如,規範的第6.7節將涵蓋了將方法轉換爲函數的方法,稱爲「方法值」。 ) 我認爲,當人們嘗試學習這門語言時,這些詞的鬆散使用導致了很多混亂。 –

+4

@我知道,我知道 - PinS是教我Scala的書。我更好地學習了困難的方法,也就是說,帕普讓我直說。 –

+3

很好的解釋!我有一件事要補充:當你引用編譯器的'val f = m'擴展爲'val f = new Function'[List [Int],AnyRef] def apply(x $ 1:List [Int ')= this.m(x $ 1) }''你應該指出'apply'方法中的'this'並不是指向AnyRef對象,而是指向valf =因爲'this'是由閉包捕獲的值(例如,如下面指出的'return'),因此m_'被評估(_outer_'this',可以這麼說)。 –

11

函數不支持參數默認值。方法呢。從方法轉換爲函數會丟失參數默認值。 (斯卡拉2.8.1)

+4

這是有理由嗎? – corazza

26

比方說您有一個List

scala> val x =List.range(10,20) 
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 

定義一個方法

scala> def m1(i:Int)=i+2 
m1: (i: Int)Int 

定義一個函數

scala> (i:Int)=>i+2 
res0: Int => Int = <function1> 

scala> x.map((x)=>x+2) 
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21) 

方法接受參數

scala> m1(2) 
res3: Int = 4 

定義功能的VAL

scala> val p =(i:Int)=>i+2 
p: Int => Int = <function1> 

參數,以功能爲選配

scala> p(2) 
    res4: Int = 4 

scala> p 
res5: Int => Int = <function1> 

參數的方法是強制性

scala> m1 
<console>:9: error: missing arguments for method m1; 
follow this method with `_' if you want to treat it as a partially applied function 

檢查以下Tutorial,說明通過其他方面的差異有類似的例子使用方法Vs函數的其他差異示例,使用函數作爲變量,創建函數即返回功能

28

功能 N的函數可以使用的參數列表產生 結果被調用。一個函數有一個參數列表,一個主體和一個結果類型。 屬於類,特徵或單例對象成員的函數是 ,稱爲方法。在其他功能中定義的功能稱爲 本地功能。結果類型爲Unit的函數稱爲過程。 源代碼中的匿名函數稱爲函數文字。 在運行時,函數文字被實例化爲對象,稱爲 函數值。

Programming in Scala Second Edition. Martin Odersky - Lex Spoon - Bill Venners

+0

函數可以作爲def或val/var屬於一個類。只有def是方法。 –

1

有一個很好的文章here從我的大部分描述都採取。 只是關於我的理解功能和方法的簡短比較。希望它有幫助:

函數: 它們基本上是一個對象。更確切地說,函數是具有應用方法的對象;因此,由於它們的開銷,它們比方法稍慢。它與靜態方法類似,它們獨立於要調用的對象。 功能的一個簡單的例子是一樣波紋管:

val f1 = (x: Int) => x + x 
f1(2) // 4 

上面的線是除了像object1 = Object2的分配一個對象到另一什麼。實際上,在我們的例子中,object2是一個匿名函數,因此左側獲得了一個對象的類型。因此,現在f1是一個對象(Function)。匿名函數實際上是Function1 [Int,Int]的一個實例,它表示一個函數,其中1個參數的類型爲Int,返回值的類型爲Int。 無參數調用F1會給我們的匿名函數(INT => INT =)

方法的簽名: 他們不是對象,但分配到一個類的實例,即,一個對象。完全一樣的,在C++中的java或成員函數(如Raffi Khatchadourian在註釋中指出到this question)方法等 的方法的一個簡單的例子是一樣波紋管:

def m1(x: Int) = x + x 
m1(2) // 4 

上面的線是不一個簡單的值賦值,但是一個方法的定義。當你像第二行一樣調用這個值爲2的方法時,x被替換爲2並且結果將被計算並且你得到4作爲輸出。在這裏你會得到一個錯誤,如果只是簡單地寫m1,因爲它是方法並且需要輸入值。通過使用_你可以將方法分配給一個函數像波紋管:

val f2 = m1 _ // Int => Int = <function1>