1

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

第一个测试对包含 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 倍:过滤期间每个元素一次,映射期间每个元素一次,以及 3 次以将 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:为什么通配符会改变调用计数器的次数,以及它是如何工作的?

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

{
    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

4

1 回答 1

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)

正如您可以想象的那样,它并不能完全按照您想要的方式进行 - 您只在开始时打印(或在您的情况下进行增量)一次。您的两个问题都源于此。

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

于 2013-03-20T21:43:31.063 回答