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

时间:2013-03-20 21:36:26

标签: scala scala-collections

我有一些测试结果,我无法解释。

第一个测试在包含4个元素的列表上执行过滤,map和reduce:

{
    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次:过滤期间每个元素一次,映射期间每个元素一次,三次加起来元素。

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

{
    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

1 个答案:

答案 0 :(得分:11)

你可能在想那个

filter {
  println
  _ > 0
}

装置

filter{ i =>
  println
  i > 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)

你可以想象的并不是你想要的方式 - 你只在开始时打印(或者在你的情况下做增量)。你的问题都源于此。

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