在 Scala 中实现通用向量 [英] Implementing a generic Vector in Scala

查看:38
本文介绍了在 Scala 中实现通用向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 Scala 中实现一个通用(数学)向量,但我遇到了一些如何正确执行的问题:

I'm trying to implement a generic (mathematical) vector in Scala, and I'm running into a couple of issues of how to do it properly:

1) 你如何处理 + 和 - 这样对 Vector[Int]Vector[Double] 的操作会返回一个 Vector[Double]?简而言之,我将如何进行数字类型的自动提升(最好利用 Scala 的自动提升)?因为使用 implicit n: Numeric[T] 只有当两个向量的类型相同时才有效.

1) How do you handle + and - such that operating on a Vector[Int] and a Vector[Double] would return a Vector[Double]? In short, how would I go about doing auto promotion of numeric types (preferably taking advantage of Scala's auto promotion)? Because using implicit n: Numeric[T] only works if the types of both vectors are the same.

2) 相关,我应该如何定义 * 操作,使其接受任何数字类型,并返回正确数字类型的向量?也就是说,一个 Vector[Int] * 2.0 将返回一个 Vector[Double].

2) Related, how should I define a * operation such that it takes in any Numeric type, and return a vector of the right numeric type? That is, a Vector[Int] * 2.0 would return a Vector[Double].

这是我当前的代码(它的行为不像我想要的那样):

This is my current code (which doesn't behave as I would want it):

case class Vector2[T](val x: T, val y: T)(implicit n: Numeric[T]) {
  import n._

  def length = sqrt(x.toDouble() * x.toDouble() + y.toDouble() * y.toDouble())
  def unary_- = new Vector2(-x, -y)

  def +(that: Vector2) = new Vector2(x + that.x, y + that.y)
  def -(that: Vector2) = new Vector2(x - that.x, y - that.y)

  def *(s: ???) = new Vector2(x * s, y * s)
}

更新

经过深思熟虑,我决定接受 Chris K 的回答,因为它适用于我问过的所有情况,尽管类型类解决方案很冗长(Scala 中的数字类型是 Byte、Short、Int、Long、Float、Double、BigInt、BigDecimal,这使得在每个可能的类型对之间实现所有操作变得非常有趣.

Update

After a lot of thought, I've decided to accept Chris K's answer, because it works for all the situations I've asked about, despite the verbosity of the type class solution (the numeric types in Scala are Byte, Short, Int, Long, Float, Double, BigInt, BigDecimal, which makes for a very fun time implementing all the operations between each possible pair of types).

我对这两个答案都投了赞成票,因为它们都是很好的答案.我真的希望 Gabriele Petronella 的答案适用于所有可能的情况,即使只是因为它是一个非常优雅和简洁的答案.我确实希望它最终会以某种方式起作用.

I've upvoted both answers, because they're both excellent answers. And I really wish Gabriele Petronella's answer worked for all possible scenarios, if only because it's a very elegant and consise answer. I do hope there'll be some way that it'll work eventually.

推荐答案

想到了一些方法:

  1. 使用类型类,示例如下
  2. 使用 Spire,一个 Scala 的数学库.可以在此处找到有关使用 spire 的矢量教程.
  3. 将类型类与 Shapeless 结合以支持任意维度的向量.阅读 Shapeless 对抽象超过 arity"的支持.
  4. 在对 Vector 调用操作之前将 Vector 转换为相同的类型.Gabriele Petronella 给出了一个很好的例子,它使用标准 Scala 库提供的隐式在 Scala 2.10 或更高版本上执行此操作.
  1. Use type classes, an example follows
  2. Use Spire, a maths lib for Scala. A tutorial for vectors using spire can be found here.
  3. Combine type classes with Shapeless to support vectors of any dimension. Read Shapeless' support for 'abstracting over arity'.
  4. Convert the Vectors to the same type before calling the operations on the Vector. Gabriele Petronella has given a great example of doing this on Scala 2.10 or later using implicits supplied by the standard Scala library.

直接使用类型类:

这种方法在您第一次创建时有点冗长,因为必须为想要支持的每个值组合创建隐式类.但这种方法是合理的.关于类型类的更多细节可以阅读 这里

This approach is somewhat verbose the first time that you create it, as one has to create implicit classes for each combination of values that one wants to support. But the approach is sound. More details about type classes can be read here

如果要将以下代码复制粘贴到 scala REPL 中,请务必先输入 ':paste'.否则当输入'a+b'时,trait 和伴生对象之间的关系不会被拾取,也不会找到隐含的.

If you want to copy and paste the following code into the scala REPL, be sure to enter ':paste' first. Otherwise the relationship between the trait and the companion object will not be picked up and the implicit will not be found when one enters 'a+b'.

trait NumberLike[A,B,C] {
  def plus(x: A, y: B): C
}
object NumberLike {
  implicit object NumberLikeIntDouble extends NumberLike[Int,Double,Double] {
    def plus(x: Int, y: Double): Double = x + y
  }
  implicit object NumberLikeDoubleInt extends NumberLike[Double,Int,Double] {
    def plus(x: Double, y: Int): Double = x + y
  }
  implicit object NumberLikeIntInt extends NumberLike[Int,Int,Int] {
    def plus(x: Int, y: Int): Int = x + y
  }
}


case class Vector2[T](val x: T, val y: T) {
  def +[B,C](that: Vector2[B])(implicit c:NumberLike[T,B,C]) : Vector2[C] = new Vector2[C](c.plus(this.x,that.x), c.plus(this.y,that.y))
}

val a = Vector2(1,2)
val b = Vector2(2.0,2.0)

a+a
a+b
b+a

要将更多运算符添加到向量中,例如减法和除法,然后将它们添加到 NumberLike 特征并使用上面的加号示例进行操作.

To add more operators to the vector, like subtraction and divide then add them to the NumberLike trait and follow it through using the plus example above.

这篇关于在 Scala 中实现通用向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆