Scala 中的通用点加法
Generic Point Addition in Scala
我正在尝试使用加法和 Scala 乘法成员来调整 Tuple(2) class,这样我就可以更轻松地将它们用作几何二维点。这就是我得到的:
implicit class Point[+T: Numeric](val t: (T, T)) {
import Numeric.Implicits._
def _1 = t._1
def _2 = t._2
def +[V >: T](p: Point[V])(implicit ev: Numeric[V]) : (V,V) =
(ev.plus(t._1, p._1), ev.plus(t._2, p._2))
}
println((2, 3) + Point(5.0, 6.0))
这不起作用(找不到 ev
)大概是因为严格来说 Float
不是 Int
的超类型 - 但实际上似乎没有是一个弱一致性约束运算符,完全将其排除在外意味着我不能再使用 ev.plus
因为它需要两个相同类型的值 V
,而不是 T
和V
之一。
如何正确实施此代码?
你完全正确。问题是 Numeric[T]
不允许根据其签名混合类型(V
和 T
):def plus(x: T, y: T): T
.
您可以 use/emulate 这个库:
https://github.com/azavea/numeric。它做了很多隐式体操以允许混合类型工作。
它完全满足您的需求:
EasyImplicits allows you to operate on mixed numeric types (e.g. T + U + Int).
或者选择使用基础 Double
类型而不是 T
/V
- 我知道这并不总是很好(精度损失等)。
您的代码仍然适用于不混合类型的点,这还不错,否则 Double
看起来并不那么糟糕,因为无论如何都会发生自动转换。
我认为因为构造函数参数是一个元组,所以它使本应简单的数字扩展变得复杂。这涵盖了许多(大多数?)转化。
implicit class Point[T: Numeric](a: (T, T)) {
import Numeric.Implicits._
val _1 = a._1
val _2 = a._2
def +(x: T, y: T) = add(_1, _2, x, y)
def +[U: Numeric](x:U, y:U)(implicit ev: T => U) =
add[U](ev(_1), ev(_2), x, y)
def +[U: Numeric](b: Point[U])(implicit ev: T => U) =
add[U](ev(_1), ev(_2), b._1, b._2)
private def add[N: Numeric](a:N, b:N, c:N, d:N) = (a+c, b+d)
}
用法:
(1, 4) + (2.2, 4.4) //res0: (Double, Double) = (3.2,8.4)
(2.1, 3.1) + (9, 2) //res1: (Double, Double) = (11.1,5.1)
(1, 4) + Point((5.2, 6.2)) //res2: (Double, Double) = (6.2,10.2)
Point((2, 3)) + (4L, 5L) //res3: (Long, Long) = (6,8)
Point((1.5, 2.9)) + (1, 1) //res4: (Double, Double) = (2.5,3.9)
但它不会加宽作为参数传递给 +()
方法的现有 Point
的元素。
(3.3, 4.5) + Point((1, 3)) //Error:No implicit view available from Double => Int.
我正在尝试使用加法和 Scala 乘法成员来调整 Tuple(2) class,这样我就可以更轻松地将它们用作几何二维点。这就是我得到的:
implicit class Point[+T: Numeric](val t: (T, T)) {
import Numeric.Implicits._
def _1 = t._1
def _2 = t._2
def +[V >: T](p: Point[V])(implicit ev: Numeric[V]) : (V,V) =
(ev.plus(t._1, p._1), ev.plus(t._2, p._2))
}
println((2, 3) + Point(5.0, 6.0))
这不起作用(找不到 ev
)大概是因为严格来说 Float
不是 Int
的超类型 - 但实际上似乎没有是一个弱一致性约束运算符,完全将其排除在外意味着我不能再使用 ev.plus
因为它需要两个相同类型的值 V
,而不是 T
和V
之一。
如何正确实施此代码?
你完全正确。问题是 Numeric[T]
不允许根据其签名混合类型(V
和 T
):def plus(x: T, y: T): T
.
您可以 use/emulate 这个库: https://github.com/azavea/numeric。它做了很多隐式体操以允许混合类型工作。
它完全满足您的需求:
EasyImplicits allows you to operate on mixed numeric types (e.g. T + U + Int).
或者选择使用基础 Double
类型而不是 T
/V
- 我知道这并不总是很好(精度损失等)。
您的代码仍然适用于不混合类型的点,这还不错,否则 Double
看起来并不那么糟糕,因为无论如何都会发生自动转换。
我认为因为构造函数参数是一个元组,所以它使本应简单的数字扩展变得复杂。这涵盖了许多(大多数?)转化。
implicit class Point[T: Numeric](a: (T, T)) {
import Numeric.Implicits._
val _1 = a._1
val _2 = a._2
def +(x: T, y: T) = add(_1, _2, x, y)
def +[U: Numeric](x:U, y:U)(implicit ev: T => U) =
add[U](ev(_1), ev(_2), x, y)
def +[U: Numeric](b: Point[U])(implicit ev: T => U) =
add[U](ev(_1), ev(_2), b._1, b._2)
private def add[N: Numeric](a:N, b:N, c:N, d:N) = (a+c, b+d)
}
用法:
(1, 4) + (2.2, 4.4) //res0: (Double, Double) = (3.2,8.4)
(2.1, 3.1) + (9, 2) //res1: (Double, Double) = (11.1,5.1)
(1, 4) + Point((5.2, 6.2)) //res2: (Double, Double) = (6.2,10.2)
Point((2, 3)) + (4L, 5L) //res3: (Long, Long) = (6,8)
Point((1.5, 2.9)) + (1, 1) //res4: (Double, Double) = (2.5,3.9)
但它不会加宽作为参数传递给 +()
方法的现有 Point
的元素。
(3.3, 4.5) + Point((1, 3)) //Error:No implicit view available from Double => Int.