2014-01-12 104 views
4

在關於創建斐波那契數字的Stackoverflow帖子中,我找到了方法#::What is the fastest way to write Fibonacci function in Scala?)。在ScalaDocs中,我找到了描述哈希冒號冒號方法的條目(參見這裏,1提取器允許使用#::模式匹配流。我該如何在Scala中使用#::/hash冒號冒號?

我意識到,我可以用斐波那契函數這樣

def fibonacci: Stream[Long] = { 
    def tail(h: Long, n: Long): Stream[Long] = h #:: tail(n, h + n) 
    tail(0, 1) 
} 
fibonacci(10)  //res4: Long = 55 
  • 我應該如何理解ScalaDocs解釋?你能舉一個例子嗎?

  • 爲什麼沒有必要在上面的fibonacci函數中定義參數?

+5

謝謝你把「哈希結腸結腸」的稱號,所以我可以從谷歌發現這:) – jackweirdy

回答

4

方法#::是爲流定義的。它類似於列表的::方法。 List和Stream之間的主要區別在於Stream的元素是懶惰評估的。

最後一行有一些scala魔術發生。實際上,首先您要評估fibonacci表達式,並返回一個Stream對象。這個流的第一個和第二個元素是0和1,如下例子的第三行,其餘的流是通過遞歸調用定義的。然後你從流中提取第十要素,它的計算結果爲55

在下面的代碼,我表現出相似的訪問列表第四的元素

val list = List(1,2,3,4,5) 
println(list(3)) // prints 4 

概括地說,想想流作爲無限的列表。你可以找到更多關於流在這裏http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream

3

在您的例子h #:: tail(n, h + n)創建一個新的數據流,其中h是流,tail(n, h + n)流將被懶洋洋地評估了頭。

另一個(也許更容易)的例子是將自然數定義爲BigInt流。

def naturalNumbers = { 
    def next(n: BigInt) : Stream[BigInt] = n #:: next(n + 1) 
    next(0) 
} 

println(naturalNumbers)會導致打印Stream(0, ?),因爲頭是嚴格的,這意味着它會一直進行評估。尾部將是next(1),僅在需要時才進行評估。

在你的例子中fibonacci(10)fibonacci.apply(10)的句法糖,它在Stream類中定義,併產生具有流中索引的元素。

你也可以用流做很多其他事情。例如獲得大於100的第一個斐波那奇數:fibonacci.dropWhile(_ <= 100).head或僅打印前100個斐波納契數println(fibonacci.take(100).toList)

3

快速回答#2是fibonacci(10)不是一個函數調用的參數,它是一個函數調用不帶參數之後的任何與參數「10返回調用」。

這本來是比較容易理解,如果這樣寫的:

scala> val s = fibonacci 
s: Stream[Long] = Stream(0, ?) 

scala> s(10) 
res1: Long = 55