4

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

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

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

编辑:

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

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

我正在寻找一种通过 epsilon 而不每次都通过 Concert 值的方法(一些“隐式”魔法)。

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

4

2 回答 2

6

啊,浮点数的乐趣。

我认为用模糊比较覆盖等于不是一个好主意。它违反了你通常认为平等的各种事情。想象 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)

请注意,隐式不是类的参数,而是操作的参数。

于 2013-06-15T11:44:14.680 回答
6

你是对的。如果您担心精度,那么您将需要覆盖等于:

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
于 2013-06-15T00:13:27.720 回答