等于具有浮点字段的案例类

时间:2013-06-14 23:53:26

标签: scala hashcode case-class

是否可以创建具有浮点字段的案例类,例如:

case class SomeClass(a:Double, b:Double) 

我猜自动生成的相等方法在这种情况下不起作用。 最重要的是最佳解决方案吗?

编辑:

如果重写等于是要走的路,我想避免硬编码epsilon(其中epsilon定义为=> | this.a-a |< epsilon)。这不会编译:

case class SomeClass(a:Double, b:Double, implicit epsilon:Double)  

我正在寻找一种方法来传递epsilon,而不是每次都传递音乐会价值 (一些“隐含的”魔法)。

我还要跟进更一般的问题,如何为只有浮点字段的类定义哈希码?

2 个答案:

答案 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不是类的参数,而是操作的参数。