2011-04-28 164 views
7

我有這種奇怪的情況,我不明白。我正在閱讀「Scala編程」一書。 9.奇怪的東西與咖喱功能

比方說,我有一個咖喱功能:

def withThis(n:Int)(op:Int=>Unit){ 
     println("Before") 
     op(n); 
     println("After") 
} 

當我用一個參數調用它一個特殊的花,語法,它按預期工作中:

withThis(5){ 
    (x) => {println("Hello!"); println(x); } 
} 
// Outputs 
Before 
Hello! 
5 
After 

但是,如果我把兩個陳述,我得到一些奇怪的:

withThis(5){ 
    println("Hello!") 
    println(_) 
} 
// Outputs 
Hello! 
Before 
5 
After 

怎麼來的「你好!」在「之前」之前打印並在內部打印「5」?我瘋了嗎?

回答

10

你最後的代碼示例應改寫爲產生預期的結果:

withThis(5) { x => 
    println("Hello!") 
    println(x) 
} 

否則,你的例子是相當於

withThis(5) { 
    println("Hello!") 
    (x: Int) => println(x) 
} 

的佔位符_會擴展爲以非退化方式儘可能緊密地結合(即,它不會擴展到println(x => x))。

要注意的另一件事是塊總是返回它的最後一個值。在你的例子中,最後的值實際上是(x: Int) => println(x)

+2

但是println(x => x)甚至不是正確的語法。無論如何 - 我明白println(_)的作用 - 對於這個「塊」業務,我更加困惑。 – drozzy 2011-04-28 18:34:20

+0

語法正確。在這種情況下,它不會編譯,因爲缺少參數類型,但如果編譯器可以推斷它,則會起作用。簡單的例子:'def doit(f:Int => Int)=(); doit(x => x)' – 2011-04-28 18:37:36

+0

我知道它相當於「(x:Int)=> println(x)」,我的問題是爲什麼它實際上首先執行println。我想你回答「塊總是返回最後一個值」。這是否意味着塊不會被懶惰評估? – drozzy 2011-05-02 16:55:24

3

在你的第二個例子中,捲曲部分:{ println("Hello!"); println(_) }是一個打印「Hello!」的塊。並返回咖啡println。想象一下,它簡化爲{ println("Hello!"); 5 },它打印「你好!」並返回5

+0

我不認爲我在書中「阻擋」了一部分。這就是爲什麼我很可能會感到困惑。 – drozzy 2011-04-28 18:09:43

+0

因此,在第二個例子中沒有辦法使它按預期工作?我嘗試使用名稱參數 - 但他們不接受一個參數。 – drozzy 2011-04-28 18:35:09

+0

只是要挑剔:塊不會返回「curried'println'」,而是一個帶有一個參數的匿名函數,該函數使用所獲取的參數調用println。 – 2011-04-28 18:45:21