我有这个函数用毕达哥拉斯计算两个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])
我正在努力重构这个方法,因此它更容易阅读,有人可以帮忙吗?
答案 0 :(得分:3)
以下是做出以下三个假设的另一种方式:
List(x, y)
或List(x, y, z)
。我们不知道如何处理List(x, z, y)
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