是否可以创建具有浮点字段的案例类,例如:
case class SomeClass(a:Double, b:Double)
我猜自动生成的相等方法在这种情况下不起作用。 最重要的是最佳解决方案吗?
编辑:
如果重写等于是要走的路,我想避免硬编码epsilon(其中epsilon定义为=> | this.a-a |< epsilon)。这不会编译:
case class SomeClass(a:Double, b:Double, implicit epsilon:Double)
我正在寻找一种方法来传递epsilon,而不是每次都传递音乐会价值 (一些“隐含的”魔法)。
我还要跟进更一般的问题,如何为只有浮点字段的类定义哈希码?
答案 0 :(得分:5)
你是对的。如果您担心精度,那么您需要覆盖equals:
case class SomeClass(a:Double, b:Double)
SomeClass(2.2 * 3, 1.0) == SomeClass(6.6, 1.0)
// res0: Boolean = false
case class BetterClass(a: Double, b: Double) {
override def equals(obj: Any) = obj match {
case x: BetterClass =>
(this.a - x.a).abs < 0.0001 && (this.b - x.b).abs < 0.0001
case _ => false
}
}
BetterClass(2.2 * 3, 1.0) == BetterClass(6.6, 1.0)
// res1: Boolean = true
答案 1 :(得分:5)
我认为用模糊比较来覆盖equals不是一个好主意。它违反了你通常认为理所当然的平等的各种事情。想象一下a,b和c是一些模糊等于的案例类。然后可以得到a,b,c使得a == b,b == c但是a!= c。
然后要考虑哈希码的行为。如果使用模糊相等覆盖equals并且不覆盖hashcode,则无法在hashmap或set中使用结果对象,因为a == b但是a.hashcode!= b.hashcode。
解决问题的最好方法是定义一个类似=〜=的运算符,除了equals / ==之外还提供模糊比较(至少对于scala中的不可变对象)意味着对象是完全相同的,所以你可以在不改变计算结果的情况下将其中一个替换为另一个。
如果您还希望能够通过隐式配置比较的精度,则会增加另一个复杂程度。这是一个更完整的例子:
// a class to configure the comparison that will be passed to the operator
// as an implicit value
case class CompareSettings(epsilon:Double = 0.1) extends AnyVal
// add an operator =~= to double to do a fuzzy comparions
implicit class DoubleCompareExtensions(val value:Double) extends AnyVal {
def =~=(that:Double)(implicit settings:CompareSettings) : Boolean = {
// this is not a good way to do a fuzzy comparison. You should have both relative
// and absolute precision. But for an example like this it should suffice.
(value - that).abs < settings.epsilon
}
}
case class SomeClass(x:Double, y:Double) {
// we need an implicit argument of type CompareSettings
def =~=(that:SomeClass)(implicit settings:CompareSettings) =
// the implicit argument will be automatically passed on to the operators
this.x =~= that.x && this.y =~= that.y
}
// usage example
val x=1.0
val y=1.01
// this won't work since there is no implicit in scope
x =~= y
// define an implicit of the right type
implicit val compareSettings = CompareSettings(0.2)
// now this will work
x =~= y
// and this as well
SomeClass(1,2) =~= SomeClass(1.1,2)
请注意,implicit不是类的参数,而是操作的参数。