关于简单数学数组运算的新手Scala问题

use*_*117 9 scala

新手斯卡拉问题:

假设我想在Scala中执行此操作[Java代码]:

public static double[] abs(double[] r, double[] im) {
  double t[] = new double[r.length];
  for (int i = 0; i < t.length; ++i) {
    t[i] = Math.sqrt(r[i] * r[i] + im[i] * im[i]);
  }
  return t;
}  
Run Code Online (Sandbox Code Playgroud)

并且它也是通用的(因为Scala有效地完成了我读过的通用基元).仅依靠核心语言(没有库对象/类,方法等),如何做到这一点?说实话,我根本看不出怎么做,所以我猜这只是一个纯粹的奖励点问题.

我遇到了很多问题,试图做这个简单的事情,我现在放弃了Scala.希望一旦我看到斯卡拉的方式,我将有一个'啊哈'的时刻.

更新:与其他人讨论这个问题,这是我迄今为止找到的最佳答案.

def abs[T](r: Iterable[T], im: Iterable[T])(implicit n: Numeric[T]) = {
   import n.mkNumericOps                                                   
   r zip(im) map(t => math.sqrt((t._1 * t._1 + t._2 * t._2).toDouble))          
}
Run Code Online (Sandbox Code Playgroud)

oxb*_*kes 13

在scala中执行泛型/高性能原语实际上涉及scala用于避免装箱/拆箱的两个相关机制(例如,包装int在a中java.lang.Integer,反之亦然):

  • @specialize 类型注释
  • 使用Manifest数组

specialize是一个注释,告诉Java编译器创建代码的"原始"版本(类似于C++模板,所以我被告知).检查Tuple2(特殊的)类型声明(List不是).它是在2.8中添加的,意味着,例如代码就像CC[Int].map(f : Int => Int)没有拳击任何ints 一样执行(假设CC是专门的,当然!).

Manifests是一种在scala 中执行reified类型的方法(受JVM 类型擦除限制).当您希望在某种类型上对某个方法进行通用化T,然后在该方法中创建T(即T[])数组时,这尤其有用.在Java中,这是不可能的,因为这new T[]是非法的.在scala中,这可以使用Manifest.特别是,在这种情况下,它允许我们构建一个原始的 T数组,如double[]int[].(这很棒,万一你想知道)

拳击是从性能的角度非常重要,因为它产生的垃圾,除非你所有的ints为<127它也很明显,在额外的工艺步骤/方法调用等方面添加了一个间接的水平,但考虑到你可能穿上"除非你绝对肯定你肯定会这样做(即大多数代码不需要这样的微优化)


所以,回到问题:为了不装箱/拆箱要做到这一点,你必须使用Array(List!还没有专门的,并会比较对象的饥饿无论如何,即使它是).zipped一对集合上的函数将返回一个Tuple2s 的集合(这不需要装箱,因为这专门的).

为了一般地这样做(即跨越各种数字类型),你必须要求在你的泛型参数上绑定一个上下文Numeric并且Manifest可以找到一个(创建数组所需).所以我开始沿着...的路线

def abs[T : Numeric : Manifest](rs : Array[T], ims : Array[T]) : Array[T] = {
    import math._
    val num = implicitly[Numeric[T]]
    (rs, ims).zipped.map { (r, i) => sqrt(num.plus(num.times(r,r), num.times(i,i))) }
    //                               ^^^^ no SQRT function for Numeric
}
Run Code Online (Sandbox Code Playgroud)

......但它不太有效.原因是"通用" Numeric值没有像sqrt- >这样的操作,所以你只能在知道你有一个这样的时候这样做Double.例如:

scala> def almostAbs[T : Manifest : Numeric](rs : Array[T], ims : Array[T]) : Array[T] = {
 | import math._
 | val num = implicitly[Numeric[T]]
 | (rs, ims).zipped.map { (r, i) => num.plus(num.times(r,r), num.times(i,i)) }
 | }
almostAbs: [T](rs: Array[T],ims: Array[T])(implicit evidence$1: Manifest[T],implicit     evidence$2: Numeric[T])Array[T]
Run Code Online (Sandbox Code Playgroud)

优秀 - 现在看到这种纯粹的通用方法做一些事情!

scala> val rs = Array(1.2, 3.4, 5.6); val is = Array(6.5, 4.3, 2.1)
rs: Array[Double] = Array(1.2, 3.4, 5.6)
is: Array[Double] = Array(6.5, 4.3, 2.1)

scala> almostAbs(rs, is)
res0: Array[Double] = Array(43.69, 30.049999999999997, 35.769999999999996)
Run Code Online (Sandbox Code Playgroud)

现在我们可以sqrt得到结果,因为我们有一个Array[Double]

scala> res0.map(math.sqrt(_))
res1: Array[Double] = Array(6.609841147864296, 5.481788029466298, 5.980802621722272)
Run Code Online (Sandbox Code Playgroud)

并证明这可以用于另一种Numeric类型:

scala> import math._
import math._
scala> val rs = Array(BigDecimal(1.2), BigDecimal(3.4), BigDecimal(5.6)); val is =     Array(BigDecimal(6.5), BigDecimal(4.3), BigDecimal(2.1))
rs: Array[scala.math.BigDecimal] = Array(1.2, 3.4, 5.6)
is: Array[scala.math.BigDecimal] = Array(6.5, 4.3, 2.1)

scala> almostAbs(rs, is)
res6: Array[scala.math.BigDecimal] = Array(43.69, 30.05, 35.77)

scala> res6.map(d => math.sqrt(d.toDouble))
res7: Array[Double] = Array(6.609841147864296, 5.481788029466299, 5.9808026217222725)
Run Code Online (Sandbox Code Playgroud)


Abh*_*kar 11

使用zipmap:

scala> val reals = List(1.0, 2.0, 3.0)
reals: List[Double] = List(1.0, 2.0, 3.0)

scala> val imags = List(1.5, 2.5, 3.5)
imags: List[Double] = List(1.5, 2.5, 3.5)

scala> reals zip imags
res0: List[(Double, Double)] = List((1.0,1.5), (2.0,2.5), (3.0,3.5))

scala> (reals zip imags).map {z => math.sqrt(z._1*z._1 + z._2*z._2)}
res2: List[Double] = List(1.8027756377319946, 3.2015621187164243, 4.6097722286464435)

scala> def abs(reals: List[Double], imags: List[Double]): List[Double] =
     | (reals zip imags).map {z => math.sqrt(z._1*z._1 + z._2*z._2)}
abs: (reals: List[Double],imags: List[Double])List[Double]

scala> abs(reals, imags)
res3: List[Double] = List(1.8027756377319946, 3.2015621187164243, 4.6097722286464435)
Run Code Online (Sandbox Code Playgroud)

UPDATE

最好使用zipped它,因为它避免了创建临时集合:

scala> def abs(reals: List[Double], imags: List[Double]): List[Double] =
     | (reals, imags).zipped.map {(x, y) => math.sqrt(x*x + y*y)}
abs: (reals: List[Double],imags: List[Double])List[Double]

scala> abs(reals, imags)
res7: List[Double] = List(1.8027756377319946, 3.2015621187164243, 4.6097722286464435)
Run Code Online (Sandbox Code Playgroud)