2016-08-01 87 views
2

我正在玩斯卡拉流。 這裏是從給定數字開始的所有整數流。
我添加了一個println來跟蹤函數的每個調用。斯卡拉流過濾器行爲

def from(n: Int): Stream[Int] = n #:: from({ println(n); n + 1 }) 

val nats = from(0)   //> nats : Stream[Int] = Stream(0, ?) 

nats.take(4).toList  //> 0 
          //| 1 
          //| 2 
          //| 3 
          //| res0: List[Int] = List(0, 1, 2, 3, 4) 

正如所料,這是我的Scala工作表的輸出。然後我創建了所有素數的流。

def sieve(s: Stream[Int]): Stream[Int] = { 
    s.head #:: sieve(s.tail.filter({ println("---"); _ % s.head != 0 })) 
}       //> sieve: (s: Stream[Int])Stream[Int] 

val primes = sieve(from(2))//> primes : Stream[Int] = Stream(2, ?) 

primes.take(4).toList  //> 2 
          //| --- 
          //| 3 
          //| 4 
          //| --- 
          //| 5 
          //| 6 
          //| --- 
          //| res1: List[Int] = List(2, 3, 5, 7) 

問題出在這裏。我做了什麼在我看來應該是一個小小的改變,加入x參數而不是_佔位符。令人驚訝的是,輸出是相當不同的:

def sieve(s: Stream[Int]): Stream[Int] = { 
    s.head #:: sieve(s.tail.filter(x => { println("---"); (x % s.head) != 0 })) 
}       //> sieve: (s: Stream[Int])Stream[Int] 

val primes = sieve(from(2))//> primes : Stream[Int] = Stream(2, ?) 

primes.take(4).toList  //> 2 
          //| --- 
          //| 3 
          //| --- 
          //| 4 
          //| --- 
          //| --- 
          //| 5 
          //| --- 
          //| 6 
          //| --- 
          //| --- 
          //| --- 
          //| res1: List[Int] = List(2, 3, 5, 7) 

我不明白爲什麼所有這些重複。 使用顯式參數有什麼問題?

回答

3

所不同的是因爲{ println("---"); _ % s.head != 0 }{ println("---"); x => x % s.head != 0 }的簡稱,而不是{ x => println("---"); x % s.head != 0 }

在第一種情況下,您首先撥打println然後返回該功能,所以---filter打電話打印一次;在第二次中,每個被調用的流的每個元素調用filter一次(因爲sieve是遞歸的並且再次過濾流,所以每個輸出元素最終會有多個---)。

+0

感謝您的解釋,我有什麼區別。目前還不清楚爲什麼返回的函數必須多次過濾相同的數字。 – freedev

+1

如果您編寫'{x => println(s「$ {s.head} --- $ x」),您可以更好地看到它; x%s.head!= 0}'(對於其他選項也是類似的,然後你會看到每一行打印的行是「過濾」的, –

+0

「......不清楚爲什麼返回的函數有要過濾更多次......「,這不是因爲它是返回的函數,而是因爲過濾器使用參數中的匿名函數應用於集合項目中。匿名函數(x => ...)不包含println當你使用_。 –

0

我們來比較一下這兩種情況下篩的說法,有一個近距離觀察:

s.tail.filter( { println("---");  _ % s.head != 0 }) 
s.tail.filter(x => { println("--- " + x); (x % s.head) != 0 }) 

你看到的println調用的區別?

現在輸出

First:   Second: 
//> 2   //> 2 
//| ---  //| --- 3 

關鍵的區別:

println("---"  ) 
println("--- " + x) 

在你的破折號之後追加" "+x的第二種情況,這就是案件的具體差異。

+1

@pedrofurla,統計輸出線的數量。 – jwvh

+0

@pedrofurla我已更新問題以更好地解釋問題。 – freedev