重构一个小的Scala函数

时间:2014-08-23 18:51:19

标签: scala functional-programming refactoring

我有这个函数用毕达哥拉斯计算两个n维点之间的距离。定理。

def computeDistance(neighbour: Point) = math.sqrt(coordinates.zip(neighbour.coordinates).map {
    case (c1: Int, c2: Int) => math.pow(c1 - c2, 2)
}.sum)

Point类(简化)如下所示:

class Point(val coordinates: List[Int])

我正在努力重构这个方法,因此它更容易阅读,有人可以帮忙吗?

3 个答案:

答案 0 :(得分:3)

以下是做出以下三个假设的另一种方式:

  1. 列表的长度是点的维数
  2. 每个列表都是正确排序的,即List(x, y)List(x, y, z)。我们不知道如何处理List(x, z, y)
  3. 所有列表长度相等
  4. def computeDistance(other: Point): Double = sqrt(
      coordinates.zip(other.coordinates)
      .flatMap(i => List(pow(i._2 - i._1, 2)))
      .fold(0.0)(_ + _)
    )
    

    这里明显的缺点是我们在列表长度方面没有任何安全性。对此的快速解决方法是让函数返回Option[Double],如下所示:

    def computeDistance(other: Point): Option[Double] = {
      if(other.coordinates.length != coordinates.length) {
        return None
      }
      return Some(sqrt(coordinates.zip(other.coordinates)
        .flatMap(i => List(pow(i._2 - i._1, 2)))
        .fold(0.0)(_ + _)
       ))
    

    如果有一种类型安全的方法来确保列表长度相等,我很好奇。

    <强> 修改

    我礼貌地指出,flatMap(x => List(foo(x)))相当于map(foo),当我最初玩这个时,我忘了重构。稍微清洁的版本w / Map而不是flatMap

    def computeDistance(other: Point): Double = sqrt(
      coordinates.zip(other.coordinates)
      .map(i => pow(i._2 - i._1, 2))
      .fold(0.0)(_ + _)
    )
    

答案 1 :(得分:1)

你的大多数问题都是你试图用很长的变量名进行数学运算。它几乎总是很痛苦。数学家使用单个字母的原因就在于此。并分配临时变量。

试试这个:

class Point(val coordinates: List[Int]) { def c = coordinates }
import math._
def d(p: Point) = {
  val delta = for ((a,b) <- (c zip p.c)) yield pow(a-b, dims)
  sqrt(delta.sum)
}

答案 2 :(得分:0)

考虑类型别名和案例类,如下所示,

type Coord = List[Int]

case class Point(val c: Coord) {
  def distTo(p: Point) = {
    val z = (c zip p.c).par
    val pw = z.aggregate(0.0) ( (a,v) => a + math.pow( v._1-v._2, 2 ), _ + _ )
    math.sqrt(pw)
  }
}

所以对于任何两点,例如,

val p = Point( (1 to 5).toList )
val q = Point( (2 to 6).toList )

我们有那个

p distTo q
res: Double = 2.23606797749979

注意方法distTo在并行化的元组集合上使用aggregate,并将部分结果与最后一个参数(求和)组合在一起。对于高维点,这可能比顺序对应物更有效。

为了简化使用,还要考虑隐含类,如上面评论中所建议的

implicit class RichPoint(val c: Coord) extends AnyVal {
  def distTo(d: Coord) = Point(c) distTo Point(d)
}

因此

List(1,2,3,4,5) distTo List(2,3,4,5,6)
res: Double = 2.23606797749979