新手斯卡拉问题:
假设我想在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
使用zip和map:
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)