使用Scala集合时出现奇怪的结果
我有一些测试的结果,我不能很好地解释 第一个测试在包含4个元素的列表上进行筛选、映射和缩减:使用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 =>
{
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-是的,你是对的,视图不是这样工作的。在需要结果之前,它不会进行评估,但如果你需要三个不同的结果,它不会将它们合并到一个过程中。你真的应该接受雷克斯·克尔的答案,而不仅仅是赞美的评论!我会的,但经验表明,等待其他答案是值得的,可能会更好!你真的应该接受雷克斯·克尔的回答,而不仅仅是赞美的评论!我会的,但经验表明,等待其他答案是值得的,可能会更好!