0

我有一个名为 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)))

我的问题是有什么方法可以通过一个隐式转换函数来实现这一点来简化问题?

作为一个相关问题,如果我希望能够传递一个整数数组而不是双精度数,那么最好的方法是什么?

问候

德斯

4

1 回答 1

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的类型的元素数组”。编译器查找此类视图的一个地方是目标类型的伴随对象,因此. 这是隐式方法的好地方。PPointobject Point


第二种可能性是使用磁铁图案。您无需使用 in 中的视图逐点转换,而是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未调用该方法时我们可以保存实际的转换操作。

于 2013-10-18T13:41:10.183 回答