2016-12-25 81 views
1

可以說我們有一個函數def fun(x: X): X => Y,我們通過fun _而不是fun將這個函數作爲參數傳遞給另一個函數。我明白fun _實際上是一個函數值,而fun是指一個函數定義。在Scala中,樂趣_和樂趣之間的區別是什麼

例如讓:

val a = List(1,2,3,4) 

def fun(x: Int) = {println(x); x + 1} 

然後運行:

//This line works 
a.map(fun _) 

//This one also works, even though "fun" is not a function value 
a.map(fun) 

它們具有相同的輸出:

1 
2 
3 
4 
resX: List[Int] = List(2, 3, 4, 5) 

在大多數情況下,他們似乎工作一樣,是有沒有任何函數值不等於函數定義的例子?

+1

'fun'是一種方法,而不是一種功能。區別很重要。 – rightfold

+3

的可能的複製[什麼是Scala的ETA膨脹?](http://stackoverflow.com/questions/39445018/what-is-the-eta-expansion-in-scala) – Haspemulator

+0

發佈一個實際的例子示出了「我們通過這個函數作爲另一個函數的參數,使用'fun _'而不是'fun'「和」大部分它們看起來都是一樣的「。這對我們來說會更容易。 http://stackoverflow.com/help/mcve – YoungSpice

回答

2

正如其他人在評論中指出的,當您需要一個值(方法本身沒有值)時,您需要使用fun _語法(其執行eta expansion)。在映射(或其他功能性上下文)的上下文中,隱式地在該方法上執行eta擴展。在某些情況下,eta擴展必須手動觸發。

作爲其中需要顯式ETA膨脹的具體示例,考慮這個有效片段:

def f1(x: Int): Int = 2*x 

def f2(x: Int): Int = 3*x 

val fList1 = List(f1 _, f2 _) 

fList1.map(_(2)) // List(4, 6) 

與此相對無效代碼段。

val fList2 = List(f1, f2) 
+1

'val fList2:List [Int => Int] = List(f1,f2)'也可以。 –

+0

是否有隱式轉換? –

+0

編譯器每當它看到一個預期函數的方法時執行η-擴展,在這種情況下'List'元素的類型應該是'Int => Int'類型。 –

4

signature of map,你可以看到,它的期待

「功能」 應用到每一個元素

但在你的代碼,fun是一個普通的方法在一個班級。所以,當你這樣做:

a.map(fun _) 

明確要求ETA-擴大。當你這樣做:

a.map(fun) 

隱含要求ETA-擴大。

由於fun是一個「方法」,並且在一個Function型預期的地方正在被使用,它是automagically converted爲該類型。基本上是這樣的:

new Function1[Int, Int] { 
    def apply(x: Int): Int = fun(x) 
} 

這種轉變名fun轉換爲Function被稱爲eta-expansion。詳情請參閱documentation

不幸的是,有多種方式來做你在做什麼 - a.map(fun),a.map(fun _),a.map(fun(_))a.map(x => fun(x))。這是Scala中那些經常出現的場景之一,您可以自己明確地做某些事情,或者明確要求編譯器爲您做,或者讓編譯器隱式執行。他們可能因爲隱含而有不同的行爲,並且可能是混淆的主要來源。另外,_在語言中嚴重超載,只會增加混淆。所以我一般使用隱含的行爲。