使用Scala集合时出现奇怪的结果

使用Scala集合时出现奇怪的结果,scala,scala-collections,Scala,Scala Collections,我有一些测试的结果,我不能很好地解释 第一个测试在包含4个元素的列表上进行筛选、映射和缩减: { val counter = new AtomicInteger(0) val l = List(1, 2, 3, 4) val filtered = l.filter{ i => counter.incrementAndGet() true } val mapped = filtered.map{ i =>

我有一些测试的结果,我不能很好地解释

第一个测试在包含4个元素的列表上进行筛选、映射和缩减:

{
    val counter = new AtomicInteger(0)
    val l = List(1, 2, 3, 4)
    val filtered = l.filter{ i =>
        counter.incrementAndGet()
        true
    }
    val mapped = filtered.map{ i =>
        counter.incrementAndGet()
        i*2
    }
    val reduced = mapped.reduce{ (a, b) =>
        counter.incrementAndGet()
        a+b
    }
    println("counted " + counter.get + " and result is " + reduced)
    assert(20 == reduced)
    assert(11 == counter.get)
}
计数器按我的预期增加了11倍:过滤期间每个元素增加一次,映射期间每个元素增加一次,将4个元素相加三次

使用通配符,结果会发生变化:

{
    val counter = new AtomicInteger(0)
    val l = List(1, 2, 3, 4)
    val filtered = l.filter{
        counter.incrementAndGet()
        _ > 0
    }
    val mapped = filtered.map{
        counter.incrementAndGet()
        _*2
    }
    val reduced = mapped.reduce{ (a, b) =>
        counter.incrementAndGet()
        a+b
    }
    println("counted " + counter.get + " and result is " + reduced)
    assert(20 == reduced)
    assert(5 == counter.get)
}
我不知道如何在reduce代码未编译的情况下使用通配符,但是现在,计数器只增加了5倍

那么,问题1:为什么通配符会改变计数器被调用的次数,这是如何工作的呢

然后是我的第二个相关问题。我对视图的理解是,它们会懒洋洋地执行传递给monadic方法的函数,但下面的代码没有显示这一点

{
    val counter = new AtomicInteger(0)
    val l = Seq(1, 2, 3, 4).view
    val filtered = l.filter{
        counter.incrementAndGet()
        _ > 0
    }
println("after filter: " + counter.get)
    val mapped = filtered.map{
        counter.incrementAndGet()
        _*2
    }
println("after map: " + counter.get)
    val reduced = mapped.reduce{ (a, b) =>
        counter.incrementAndGet()
        a+b
    }
println("after reduce: " + counter.get)
    println("counted " + counter.get + " and result is " + reduced)
    assert(20 == reduced)
    assert(5 == counter.get)
}
输出为:

after filter: 1
after map: 2
after reduce: 5
counted 5 and result is 20
问题2:为什么会立即执行这些功能


我使用的是Scala 2.10,你可能会这么想

filter {
  println
  _ > 0
}
意味着

但Scala还有其他想法。原因是

{ println; _ > 0 }
是一条语句,它首先打印某些内容,然后返回>0函数。因此,它将您所做的解释为指定函数的有趣方式,相当于:

val p = { println; (i: Int) => i > 0 }
filter(p)
这反过来相当于

println
val temp = (i: Int) => i > 0   // Temporary name, forget we did this!
val p = temp
filter(p)
正如你所能想象的那样,这并不完全符合你想要的方式,你只需要打印,或者在你的情况下,在开始时做一次增量。你的两个问题都源于此


如果使用下划线表示填充参数,请确保只有一个表达式!如果您使用多个语句,最好坚持使用显式命名的参数。

您可能会这样想

filter {
  println
  _ > 0
}
意味着

但Scala还有其他想法。原因是

{ println; _ > 0 }
是一条语句,它首先打印某些内容,然后返回>0函数。因此,它将您所做的解释为指定函数的有趣方式,相当于:

val p = { println; (i: Int) => i > 0 }
filter(p)
这反过来相当于

println
val temp = (i: Int) => i > 0   // Temporary name, forget we did this!
val p = temp
filter(p)
正如你所能想象的那样,这并不完全符合你想要的方式,你只需要打印,或者在你的情况下,在开始时做一次增量。你的两个问题都源于此


如果使用下划线表示填充参数,请确保只有一个表达式!如果使用多个语句,最好坚持使用显式命名的参数。

有没有办法使用视图/流使Scala在元素上迭代一次,但将所有函数应用于元素,以获得相同的结果?或者您必须以不同的方式编写代码?@JohnSmith-Filter、map和reduce具有足够不同的语义,不,您无法以任何简单的方式将它们组合起来。当然,您可以迭代一次,然后手动进行筛选、映射和缩减。@JohnSmith:.视图应该惰性地筛选和映射元素。因此,调用.reduce应该只在列表上迭代一次。其他人告诉我,但是测试并不一致,如果您查看源代码,我不确定它是否会。当然,它是懒惰的,但它似乎仍然对每个一元函数进行迭代。@JohnSmith-是的,你是对的,视图不是这样工作的。在需要结果之前,它不会进行计算,但如果需要三个不同的结果,它不会将它们合并到一个过程中。是否有任何方法使用视图/流使Scala在元素上迭代一次,但将所有函数应用于元素,以获得相同的结果?或者您必须以不同的方式编写代码?@JohnSmith-Filter、map和reduce具有足够不同的语义,不,您无法以任何简单的方式将它们组合起来。当然,您可以迭代一次,然后手动进行筛选、映射和缩减。@JohnSmith:.视图应该惰性地筛选和映射元素。因此,调用.reduce应该只在列表上迭代一次。其他人告诉我,但是测试并不一致,如果您查看源代码,我不确定它是否会。当然,它是懒惰的,但它似乎仍然对每个一元函数进行迭代。@JohnSmith-是的,你是对的,视图不是这样工作的。在需要结果之前,它不会进行评估,但如果你需要三个不同的结果,它不会将它们合并到一个过程中。你真的应该接受雷克斯·克尔的答案,而不仅仅是赞美的评论!我会的,但经验表明,等待其他答案是值得的,可能会更好!你真的应该接受雷克斯·克尔的回答,而不仅仅是赞美的评论!我会的,但经验表明,等待其他答案是值得的,可能会更好!