2014-10-30 19 views
3

我正在閱讀"Functional Programming in Scala"這本書,並且遇到了一個我不完全瞭解的例子。Scala名稱參數的使用

在上嚴格的章節/懶惰作者描述流的結構,並有這樣的代碼:

sealed trait Stream[+A] 
case object Empty extends Stream[Nothing] 
case class Cons[+A](h:() => A, t:() => Stream[A]) extends Stream[A] 

object Stream { 
    def cons[A](hd: => A, tl: => Stream[A]) : Stream[A] = { 
     lazy val head = hd 
     lazy val tail = tl 
     Cons(() => head,() => tail) 
    } 
    ... 
} 

我的問題是在智能構造(cons),它調用構造函數Cons案例分類。用來通過headtail vals的特定語法對我來說沒有意義。爲什麼不這樣調用構造函數:

Cons(head, tail) 

據我瞭解語法使用的是強要2名Function0對象只返回headtail丘壑的創建。這與剛剛通過headtail(沒有() =>前綴)有什麼不同,因爲Cons案例類已被定義爲無論如何都採用這些參數?這不是多餘的嗎?或者我錯過了什麼?

回答

8

區別在於=> A不等於() => A

前者是通過名稱傳遞,而後者是一個函數,沒有參數(單位),並返回一個A.

可以在Scala中REPL測試了這一點。

scala> def test(x: => Int):() => Int = x 
<console>:9: error: type mismatch; 
found : Int 
required:() => Int 
     def test(x: => Int):() => Int = x 
             ^

在我的示例中簡單引用x會導致參數被調用。在你的示例中,它構造了一個延遲x調用的方法。

+0

是的。對於那個很抱歉。正如我提到的@Jesper,我應該注意到兩個構造函數的區別,並沒有。所以'Cons'需要一個明確的Function0和'cons'爲你構建一個(在幕後)。什麼傳遞給'Cons'會立即被評估,傳遞給'cons'的是稍後評估的。是對的嗎? – melston 2014-10-30 21:33:54

+0

是的。當你按照價值傳遞時,它每次讀取時都會被重新評估。 – Nate 2015-10-05 22:01:35

8

首先,您假設=> A() => A是相同的。但是,他們不是。例如,=> A只能在按名稱傳遞參數的情況下使用 - 不可能聲明=> A類型的val。由於case class參數始終爲val s(除非明確聲明爲var s),因此很明顯爲什麼case class Cons[+A](h: => A, t: => Stream[A])不起作用。

其次,只是包裝了一個用名字參數與空參數列表的功能是不一樣的東西上面的代碼實現:使用lazy val時,則確保兩個hdtl在評估最多一次。如果代碼讀

Cons(() => hd,() => tl) 

原始hd一個Cons對象的每個的h方法(場)時間將被評估被調用。使用lazy valhd僅在第一次調用此Cons對象的h方法時進行評估,並且在隨後的每次調用中都會返回相同的值。

演示在REPL在精簡時尚的區別:

> def foo = { println("evaluating foo"); "foo" } 
> val direct :() => String =() => foo 
> direct() 
evaluating foo 
res6: String = foo 
> direct() 
evaluating foo 
res7: String = foo 
> val lzy :() => String = { lazy val v = foo;() => v } 
> lzy() 
evaluating foo 
res8: String = foo 
> lzy() 
res9: String = foo 

注意如何在lzy()第二次調用的「評估foo」的輸出走了,而不是的direct()第二次調用。

+0

我不知道爲什麼'val'不能是'=> A'類型。我迷失在雜草中,沒有意識到「缺點」和「缺點」的定義之間的差異。不過,我*瞭解了'lazy val'對象的使用。謝謝。 – melston 2014-10-30 21:37:15

+0

沿着同樣的路線,不是'hd'和'tl' val參數嗎?他們*是*名稱。 – melston 2014-10-30 21:44:06

+0

我不知道'val'參數是什麼意思。對於我來說,術語'val'參數在類('類A(x:String)'與'類B(val x:String)'')的上下文中唯一有意義。正如你所指出的那樣,「缺點」是一種正常的方法。那麼爲什麼'hd'和'tl'是'val'參數? – misberner 2014-10-30 21:55:43

1

請注意,方法cons的參數是名稱參數(hdtl)。這意味着如果您致電cons,則在調用cons之前不會評估參數;他們將在稍後進行評估,目前您在cons之內使用它們。

請注意,Cons構造函數使用Unit => A類型的兩個函數,但而不是作爲名稱參數。所以這些將在您調用構造函數之前進行評估。

如果你這樣做Cons(head, tail)然後headtail將被評估,這意味着hdtl進行評估。

但這裏的整點是爲了避免調用hdtl直到必要(當有人在Cons對象訪問ht)。因此,您將兩個匿名函數傳遞給構造函數Cons;這些功能將不會被調用,直到有人訪問ht

+0

我迷失在雜草中,應該注意到case類構造函數和智能構造函數聲明之間的區別。謝謝。 – melston 2014-10-30 21:30:25