【问题标题】:How would I get the sum of squares of two Lists in Scala?如何在 Scala 中获得两个列表的平方和?
【发布时间】:2011-10-25 01:46:09
【问题描述】:

如果我有两个列表

val first = List(1, 2, 3)
val second = List(4, 5, 6)

我如何获得以下内容?

(1-4)^2 + (2-5)^2 + (3-6)^2

【问题讨论】:

    标签: list scala


    【解决方案1】:

    压缩、映射和求和:

    first.view.zip(second).map(t => t._1 - t._2).map(x => x*x).sum
    
    • zip 将两个列表的元素组合成一个元组
    • view 用于延迟计算列表,以免在两个映射调用之间构建结构

    (编辑以将reduceLeft 替换为sum


    看完了cmets,我觉得我必须回来解释一下观点。基本上,视图将Traversable 转换为类似迭代器的结构,以便在应用mapzip 和其他一些方法时不必创建多个中间结构。 GenIteratableViewLike 的类型成员让我们了解哪些操作有特殊处理。所以通常如果你有一堆按顺序应用的 map、filter、drop、takeWhile,你可以使用 view 来获得一些性能。经验法则是尽早应用view 以最小化创建中间List 的数量,如有必要,最后使用force 以返回List(或您正在使用的任何集合)。因此丹尼尔的建议。

    关于性能的问题在于,在实践中,如果这很重要,您就必须进行现实检查。以下是一些数字(越低越好):

    no view List(62, 62, 62, 62, 63) sum: 311
    view before zip List(32, 31, 15, 16, 31) sum: 125
    view after zip List(31, 46, 46, 31, 31) sum: 185
    iterator List(16, 16, 16, 16, 15) sum: 79
    zipped List(62, 47, 62, 46, 47) sum: 264
    

    代码在这里:

    import testing.Benchmark
    
    def lots[T](n: Int, f: => T): T = if (n > 0) { f; lots(n - 1, f) } else f
    
    def bench(n: Int, id: String)(block: => Unit) {
      val times = (new testing.Benchmark { 
        def run() = lots(10000, block)
      }).runBenchmark(n)
      println(id + " " + times + " sum: " + times.sum)
    }
    
    val first = List(1, 2, 3)
    val second = List(4, 5, 6)
    
    bench(5, "no view") { first.zip(second).map(t => t._1 - t._2).map(x => x*x).sum }
    bench(5, "view before zip") { first.view.zip(second).map(t => t._1 - t._2).map(x => x*x).sum }
    bench(5, "view after zip") { first.zip(second).view.map(t => t._1 - t._2).map(x => x*x).sum }
    bench(5, "iterator") { first.iterator.zip(second.iterator).map(t => t._1 - t._2).map(x => x*x).sum }
    bench(5, "zipped") { (first, second).zipped.map((a,b) => a - b).map(x => x*x).sum }
    

    【讨论】:

    • viewzip 之前不是更好吗?另外,另一种选择是zipped
    • 为什么在 zip 之前视图会更好?
    • @GKelly,first.zip(second).view 将创建一个完整列表然后转换为视图,而first.view.zip(second) 将提前一步应用视图优化。
    【解决方案2】:

    一般来说,如果函数很复杂,您可能希望定义一个函数,而不是应用多个映射。您可以将其作为辅助方法执行,也可以匿名内联。

    def op(i: Int, j: Int) = { val m = i - j; m * m }
    
    (first, second).zipped.map(op).sum
    

    或者

    (first, second).zipped.map( (i, j) => { val m = i - j; m * m } ).sum
    

    zipped 在这些情况下比zip 更方便一点,因为它可以映射到一个带有 2 个参数的函数,而不是你必须使用 ._1._2 的单个元组字段。

    【讨论】:

    • 恕我直言,写(first, second).zipped.map {(x,y) => (x+y)^2}.sum更易读、更简洁。
    • @bse: ^ 在 Scala(和 Java)中是异或,而不是权力。
    • 哎呀,我的错。猜猜我最近做的 Haskell 太多了。
    【解决方案3】:

    左折叠:

    (0 /: first.zip (second)) ((a, b) => a + (b._1 - b._2)*(b._1 - b._2))
    

    以 zip 开头。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-20
      • 2012-10-24
      • 1970-01-01
      • 2020-03-17
      • 2018-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多