【问题标题】:Polymorphic dot product in Scala and anonymous function shorthandScala中的多态点积和匿名函数速记
【发布时间】:2012-01-08 02:32:51
【问题描述】:

我想通过以下方式在 Scala 中实现“矩阵点积”:

type Real = Double
type Row = Array[Real]
type Matrix = Array[Row]

def dot[T](f: (T,T) => Real)(as: Iterable[T], bs: Iterable[T]): Real =
  (for ((a, b) <- as zip bs) yield f(a, b)) sum

def rowDot(r1: Row, r2: Row) = dot(_*_)(r1, r2)
def matDot(m1: Matrix, m2: Matrix) = dot(rowDot)(m1, m2)

但是,rowDot 的定义不起作用。 Scala 需要对匿名函数 (_*_) 进行显式类型注释,所以我必须编写

def rowDot(r1: Row, r2: Row) = dot((x:Real, y: Real) => x*y)(r1, r2)

def rowDot = dot((x:Real, y: Real) => x*y) _

有没有办法改变点的定义,以便使用简写(_*_)

编辑:另一个困惑:matDot 在某些情况下也会给出类型错误。它在数组数组中失败,但在数组列表中失败

scala> matDot(Array(Array(1.0,2.0)), Array(Array(1.0,2.0,3.0)))
<console>:27: error: type mismatch;
 found   : Array[Array[Double]]
 required: Iterable[Iterable[Real]]
              matDot(Array(Array(1.0,2.0)), Array(Array(1.0,2.0,3.0)))
                          ^

scala> matDot(List(Array(1.0,2.0)), List(Array(1.0,2.0,3.0)))
res135: Real = 5.0

有什么区别?

【问题讨论】:

  • 只是一个警告:您编写的代码中有很多装箱和拆箱。它会起作用,但不会很快。
  • 很高兴知道。你能指出我改进它的正确方向吗?在使用它的代码中,我以一种重要的方式利用了具有不同长度列表的 zip 行为,因此我宁愿不简单地使用一些专用的线性代数库。
  • 不幸的是,没有好的方向。当在这两个目标之间进行权衡时,Scala 的库旨在实现最大的表达能力,而不是最大的计算效率。您可以查看 Scalala 的原生 Scala 线性代数包,或各种 Java 包(基准为 code.google.com/p/java-matrix-benchmark/wiki/…),您提供的包装器还实现了您需要的功能(例如 zip)。但是,如果矩阵运算的性能并不重要,您可能不必担心。
  • 我刚刚看到(并试图回答)您的编辑

标签: scala types polymorphism anonymous-function


【解决方案1】:

是的 - 如果您切换参数列表。当函数参数在最后一个参数列表中单独存在时,函数参数的类型推断会更有效:

def dot[T](as: Iterable[T], bs: Iterable[T])(f: (T,T) => Real): Real =
  (for ((a, b) <- as zip bs) yield f(a, b)) sum

def rowDot(r1: Row, r2: Row) = dot(r1, r2)(_*_)

【讨论】:

    【解决方案2】:

    明确指定dot[Real] 也应该有效。

    def rowDot(r1: Row, r2: Row) = dot[Real](_*_)(r1, r2)
    

    编辑

    回复您的编辑:我认为问题在于当您有Array[Array] 时,不会递归应用从ArrayWrappedArray 的隐式转换。

    Array[Int] 不是Iterable[Int];通常,当您将其分配给 Iterable 时,Array[Int] 会隐式转换为 WrappedArray[Int](其中 WrappedArray Iterable[Int])。这就是当您使用 List[Array[Int]] 时会发生的情况(您会隐式获得 List[WrappedArray[Int]])。

    但是,正如我所说,隐式转换不会递归应用,因此Array[Array[Int]] 不会隐式转换为WrappedArray[WrappedArray[Int]]

    这是一个演示问题的 REPL 会话:

    一个List[Array[Int]]可以赋值给Iterable[Iterable[Int]](注意Array被转换为WrappedArray)

    scala> val i : Iterable[Iterable[Int]] = List(Array(1,2), Array(1,2,3))
    i: Iterable[Iterable[Int]] = List(WrappedArray(1, 2), WrappedArray(1, 2, 3))
    

    一个 Array[Array[Int]] 不能自动工作(如你所见)

    scala> val j : Iterable[Iterable[Int]] = Array(Array(1,2), Array(1,2,3))
    <console>:9: error: type mismatch;
     found   : Array[Array[Int]]
     required: Iterable[Iterable[Int]]
           val j : Iterable[Iterable[Int]] = Array(Array(1,2), Array(1,2,3))
                                                  ^
    

    但是,通过一些手动操作(手动将内部数组转换为 WrappedArrays),一切都会再次运行:

        scala> import scala.collection.mutable.WrappedArray
        import scala.collection.mutable.WrappedArray
    
        scala> val k : Iterable[Iterable[Int]] = Array(WrappedArray.make(Array(1,2)),
     WrappedArray.make(Array(1,2,3)))
        k: Iterable[Iterable[Int]] = WrappedArray(WrappedArray(1, 2), WrappedArray(1, 2,
         3))
    

    【讨论】:

    • 感谢您的回答,以及对 Array 和 Iterable 的澄清。 Array 不可迭代的事实似乎很奇怪——这是将数组表示为 Java 数组的必然结果吗?
    • 是的,它是为了与 java 的互操作性。 Scala 保留了 java 类型并隐式地将它们转换为更丰富的 scala 对应物(另请参见 string 以获取另一个示例)
    猜你喜欢
    • 1970-01-01
    • 2011-02-23
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    相关资源
    最近更新 更多