2011-01-29 56 views
34

我看Runar Bjarnason present Functional Programming for Beginners,並在14:45,他定義了一個方法:功能VS Scala的方法

def isDivisibleBy(k: Int): Int => Boolean = i => i % k == 0 

和功能:

val isEven = isDivisibleBy(2) 

什麼是定義isEven的利弊作爲一種功能而不是一種方法?

我已閱讀Scala Functions vs Methods以及Difference between method and function in Scala,我理解的語義差異,但我不知道是否有在這種情況下,一些深層次的原因,爲什麼一個功能可能會或可能不會優於使用方法:

def isEven = isDivisibleBy(2) 
+0

想想吧,在相應的Java(Scala的功能與方法不就是一個很好的工作),以及它如何適合的工作時間。這是另一種方法的好處。還要注意,'def'的作用域很重要,因爲它到處都不一樣(它只是一個「方法」 - 拋開實現細節 - 在類級別上)。 – 2011-01-29 22:15:35

回答

46

在引擎蓋下,功能和方法之間還有其他差異。一般來說,普通方法比函數產生的開銷要小(這在技術上是一個帶有方法apply的對象)。

但是,如果你儘量不要去計較這些差異,認爲defvalvar領域有不同的語義,那麼它僅僅是def評估每次它被調用,而val計算一次的時間。

因此,val isEven = isDivisibleBy(2)應該在其定義期間調用isDivisibleBy(2)並指定isDivisibleBy(2)的結果。例如。它取代了k

def isDivisibleBy(k: Int): Int => Boolean = i => i % k == 0 

2和最終表達式的結果分配(在這種情況下,只有一個表達式):在另一方面

val isEven: Int => Boolean = i => i % 2 == 0 

def isEven確實沒有這樣的評價和每次都會調用isDivisibleBy(2)。

這意味着,以後,當你執行代碼,isEven(11)生成的val

11 % 2 == 0 

,並在def的情況下,你必須

isDivisibleBy(2)(11) 

只有經過評估isDivisibleBy你會得到結果。

可以添加一些調試代碼到isDivisibleBy看出差別:

def isDivisibleBy(k: Int): Int => Boolean = { 
    println("evaluating isDivisibleBy") 
    i => i % k == 0 
} 
5

我認爲將函數isEven定義爲val的主要功能是向觀衆展示可以用這種方式定義函數。然後很明顯,函數就像scala中的其他所有對象一樣。但在非演示編程的世界中,不需要編寫val s的函數。

+0

它不是一個函數定義,而是一個帶有函數結果的方法的應用程序:它顯示一個函數(Int => Boolean)可以作爲普通對象進行存儲/處理。 `val square =(x)=> x * x`將是一個定義,它也顯示了同樣的事情。兩種方法之間的細微差別(**方法是對象綁定的,允許重載和重寫**(用Java語言)和**函數不是方法**)是它們變得「有趣」的地方。 – 2011-01-29 22:08:26

+0

我對「函數定義」是什麼都有模糊的理解,因爲它很難谷歌:-),所以我以直觀的方式使用它。 'val square ...'有什麼不同?它也接受一個參數並創建一個函數,通過將`*`方法應用於`x`來返回一個對象。我試圖徹底閱讀這個問題並回答這個特定的問題。 「在這種情況下」是至關重要的,問題是提到一名男子在演講時說話,他的觀點是imho表現出不同的「對待」方法/功能的方式,問題不在於方法和功能對象之間的細微差別。 – coubeatczech 2011-01-30 00:18:47

+0

一個方法,如果一個Java的工件(也許Scala會擁有它們,如果不是Java的一部分) - 對象有*方法*(這些在類級別用`def`定義)。 Scala中的函數只是一個具有`apply`方法的對象的特殊形式(參見FunctionN等)(函數中的`def`不是方法*) - 實際上,它們可以被實現,但是這是一個實現細節,因爲這沒有公開)。Scala只是把一個*方法*轉換成一個*函數*(或將一個*函數*存儲在一個變量中)。在任何答案中我都沒有看到這種區別。 – 2011-01-30 20:34:46

3

def isDivisibleBy(k: Int): Int => Boolean返回一個函數,它接受一個I​​nt(i)作爲參數,並返回一個布爾值(i % k == 0)的方法。

val isEven = isDivisibleBy(2)另一方面是存儲由isDivisibleBy(2)返回的函數的字段。如果使用def而不是val,則每次調用isEven方法時都會調用isDivisibleBy方法,但現在只調用一次,結果存儲在該字段中。

您可以通過編寫def isEven(i: Int): Boolean = i % 2 == 0

我認爲例子的一點是,你可以擁有返回其他函數的函數達到同樣的效果,你可以存儲函數作爲對象,然後叫他們彷彿他們是傳統上定義的方法。上面的代碼也非常類似於currying,所以這也可能是該示例演示的一件事(儘管它不使用Scala's syntax for currying)。

13

我想解決另一個問題。這isEven定義爲一個方法:

def isEven = isDivisibleBy(2) 

isEven定義爲一種方法,以及:

val isEven = isDivisibleBy(2) 

在這兩種情況下,isEven是,調用時,返回功能的方法。

在第一種情況下,每次調用時都會調用isEven。例如,該呼叫isDivisible(2)三次:

def isEven = isDivisibleBy(2) 
List(1,2,3).filter(isEven) 

在第二種情況下,isDivisible(2)被調用一次(在構造時,或當執行在定義該行),並且該值被檢索每次isEven是調用。下面的示例調用僅isDivisible(2)一次:

val isEven = isDivisibleBy(2) 
List(1,2,3).filter(isEven)