我有一个名为Point的案例类定义如下:
case class Point(x: Double, y: Double)
和一个带点数组的函数:
def f(coords: Array[Point]) ...
我希望能够将一个双数组数组隐含地传递给我的函数。为此,我定义了以下两个隐式函数:
implicit def arrayToPoint(a: Array[Double]) = new Point(a(0), a(1))
implicit def arraysToPoints(a: Array[Array[Double]]) = a map(p => Point(p(0), p(1)))
我的问题是,有什么方法可以通过单一的隐含转换函数来实现这一点,以简化问题吗?
作为一个相关的问题,如果我希望能够传递一个Ints数组而不是双打数,那么最好的方法是什么?
此致
德
答案 0 :(得分:1)
您的方法arraysToPoints
是多余的。您可以在方法f
的数组参数上使用绑定视图,并将转换添加到Point
的伴随对象,如下所示:
object Point {
implicit def arrayToPoint[A](a: Array[A])(implicit view: A => Double): Point =
Point(a(0), a(1))
}
case class Point(x: Double, y: Double)
def f[P](coords: Array[P])(implicit view: P => Point): Unit = coords.foreach { p =>
println(p: Point)
}
f(Array(Point(1, 2), Point(2, 3)))
f(Array(Array(1.0, 2.0), Array(3.0, 4.0)))
f(Array(Array(1, 2), Array(3, 4)))
为了覆盖Int
和Double
的两个数组,我使用了arrayToPoint
方法上绑定的第二个视图。否则,您需要两个单独的Array[Int]
和Array[Double]
转换方法。
您可以将此f
的定义读作“,”取一个P
类型的元素数组,可以将其视为类型Point
“。编译器查找此类视图的一个位置是目标类型的伴随对象,因此object Point
。这是隐式方法的好地方。
第二种可能性是使用magnet pattern。您可以一次创建一个包装器对象,而不是使用f
中的视图逐点转换。这有点漂亮,对于大型数组最小化直接Array[Double]
参数的惩罚(因为您实例化包装器一次,但不再需要再调用视图函数)。只要数组元素类型arrayToPoint
再次被视为A
,就会使用Double
。当然Double
本身也是如此,但Int
也可以看作是Double
,尽管Scala称之为数字扩展(例如,你可以说val x: Double = 33
和整数33
被隐式“加宽”为双精度。
object Points {
implicit def direct(a: Array[Point]): Points =
new Points {
val peer = a
}
implicit def indirect[A](a: Array[Array[A]])(implicit view: A => Double): Points =
new Points {
lazy val peer = a.map { c => Point(c(0), c(1)) }
}
}
trait Points {
def peer: Array[Point]
}
def f(coords: Points): Unit = coords.peer.foreach(println)
这会在伴随对象中查找从参数类型到特殊磁体类型Points
的隐式方法。我将lazy val
用于非直接数组,以便在未调用peer
方法时保存实际转换操作。