【问题标题】:Scala stream filter behaviourScala 流过滤器行为
【发布时间】:2016-08-01 19:01:52
【问题描述】:

我正在玩 scala 流。 这是从给定数字开始的所有整数的流。
我添加了一个println 来跟踪对 from 函数的每次调用。

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)

我不明白为什么所有这些重复。 使用显式参数有什么问题?

【问题讨论】:

    标签: scala stream


    【解决方案1】:

    不同之处在于{ println( "---" ); _ % s.head != 0 }{ println( "---" ); x => x % s.head != 0 } 的缩写,而不是{ x => println( "---" ); x % s.head != 0 }

    在第一种情况下,您首先调用println,然后返回函数,因此每次filter 调用都会打印一次---;在第二个中,每个filter 调用被过滤的流的每个元素一次(并且因为sieve 是递归的并再次过滤流,所以最终输出的每个元素都有多个---)。

    【讨论】:

    • 感谢您的解释,我知道有什么区别。目前尚不清楚为什么返回的函数必须过滤更多次相同的数字。
    • "...不清楚为什么返回的函数必须过滤更多次..." 这不是因为它是返回的函数,而是因为过滤器应用于集合项在参数中使用匿名函数。匿名函数 (x => ...) 在你使用 _ 时不包含 println。
    【解决方案2】:

    让我们比较一下这两种情况下 sieve 的参数,仔细看看:

    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,这就是这两种情况之间的确切区别。

    【讨论】:

    • @pedrofurla,计算输出行数。
    • @pedrofurla 我已更新问题以更好地解释问题。
    猜你喜欢
    • 2023-03-10
    • 2015-09-09
    • 2012-12-24
    • 1970-01-01
    • 2017-01-20
    • 2015-07-27
    • 2019-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多