隐式转换数组数组

时间:2013-10-18 11:06:28

标签: arrays scala

我有一个名为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数组而不是双打数,那么最好的方法是什么?

此致

1 个答案:

答案 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)))

为了覆盖IntDouble的两个数组,我使用了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方法时保存实际转换操作。