Scala 实现职能的价值平等
如何重写Scala 实现职能的价值平等,scala,equals,Scala,Equals,如何重写equals,以在特定情况下检查函数的值等价性?例如,假设我们有以下f和g函数 val f = (x: Int) => "worf" + x val g = (x: Int) => "worf" + x 我们如何使断言(f==g)通过 我试着扩展Function1,并通过生成器实现了相等,就像这样 trait Function1Equals extends (Int => String) { override def equals(obj: Any): Boole
equals
,以在特定情况下检查函数的值等价性?例如,假设我们有以下f
和g
函数
val f = (x: Int) => "worf" + x
val g = (x: Int) => "worf" + x
我们如何使断言(f==g)
通过
我试着扩展Function1
,并通过生成器实现了相等,就像这样
trait Function1Equals extends (Int => String) {
override def equals(obj: Any): Boolean = {
val b = obj.asInstanceOf[Function1Equals]
(1 to 100).forall { _ =>
val input = scala.util.Random.nextInt
apply(input) == b(input)
}
}
}
implicit def functionEquality(f: Int => String): Function1Equals = (x: Int) => f(x)
但无法获得隐式转换以使用=
,可能是由于。Scalactics的接近了
import org.scalactic.TripleEquals._
import org.scalactic.Equality
implicit val functionEquality = new Equality[Int => String] {
override def areEqual(a: Int => String, b: Any): Boolean =
b match {
case p: (Int => String) =>
(1 to 100).forall { _ =>
val input = scala.util.Random.nextInt
a(input) == p(input)
}
case _ => false
}
}
val f = (x: Int) => "worf" + x
val g = (x: Int) => "worf" + x
val h = (x: Int) => "picard" + x
assert(f === g) // pass
assert(f === h) // fail
首先,函数相等不是一个简单的主题(扰流板:它无法正确实现;请参见示例和相应的答案),但是让我们假设您的方法“为一百个随机输入断言相同的输出”这已经足够好了 重写
=
的问题是它已经为函数1
实例实现了。所以你有两个选择:
- 定义自定义特征(您的方法)并使用
=
- 使用operation
定义一个typeclass,并为isEqual
Function1
Function1
trait,而是必须将每个函数包装到自定义trait中。您这样做了,但随后尝试实现隐式转换,该转换将为您“幕后”完成从标准Function1
到Function1Equals
的转换。但正如你自己意识到的那样,这是行不通的。为什么?因为对于Function1
实例已经存在一个方法=
,所以编译器没有理由启动隐式转换。必须将每个Function1
实例包装到自定义包装器中,以便调用重写的=
下面是示例代码:
trait MyFunction extends Function1[Int, String] {
override def apply(a: Int): String
override def equals(obj: Any) = {
val b = obj.asInstanceOf[MyFunction]
(1 to 100).forall { _ =>
val input = scala.util.Random.nextInt
apply(input) == b(input)
}
}
}
val f = new MyFunction {
override def apply(x: Int) = "worf" + x
}
val g = new MyFunction {
override def apply(x: Int) = "worf" + x
}
val h = new MyFunction {
override def apply(x: Int) = "picard" + x
}
assert(f == g) // pass
assert(f == h) // fail
第二种选择是继续使用标准Function1
实例,但使用自定义方法进行相等比较。这可以通过类型类方法轻松实现:
- 定义一个通用特征
,它将具有所需的方法(我们称之为MyEquals[a]
)isEqual
- 为
Function1[Int,String]
- 定义一个helper隐式类,只要存在
的隐式实现,该类将为MyEquals[a]
类型的某些值提供方法a
(并且我们在上一步中确保了isEqual
)MyEquals[Function1[Int,String]]
trait MyEquals[A] {
def isEqual(a1: A, a2: A): Boolean
}
implicit val function1EqualsIntString = new MyEquals[Int => String] {
def isEqual(f1: Int => String, f2: Int => String) =
(1 to 100).forall { _ =>
val input = scala.util.Random.nextInt
f1(input) == f2(input)
}
}
implicit class MyEqualsOps[A: MyEquals](a1: A) {
def isEqual(a2: A) = implicitly[MyEquals[A]].isEqual(a1, a2)
}
val f = (x: Int) => "worf" + x
val g = (x: Int) => "worf" + x
val h = (x: Int) => "picard" + x
assert(f isEqual g) // pass
assert(f isEqual h) // fail
但正如我所说,保持第一种方法(使用==
)和第二种方法(使用标准Function1
trait)的优势是不可能的。然而,我认为使用=
甚至不是一种优势。请继续阅读,找出原因
这很好地说明了为什么类型类比继承更有用、更强大。与其从某个超类对象继承==
并重写它,这对于我们无法修改的类型(如Function1
)是有问题的,不如使用一个typeclass(我们称之为Equal
),它为许多类型提供了相等方法
因此,如果范围中不存在Equal[Function1]
的隐式实例,我们只需提供自己的实例(就像我在第二段代码中所做的那样),编译器就会使用它。另一方面,如果Equal[Function1]
的隐式实例确实存在于某个地方(例如,在标准库中),它对我们没有任何改变-我们仍然只需要提供自己的实例,它将“覆盖”现有实例
现在最好的部分是:这样的类型类已经存在于和中。它分别被称为Equal
和Eq
,它们都将它们的相等比较方法命名为=
。这就是为什么我早些时候说过,我甚至不会考虑使用<代码>=< /代码>作为一个优势。谁还需要==
在代码库中始终使用scalaz或CAT意味着您将依赖===
而不是==
无处不在,您的生活将变得简单(r)
但不要指望函数相等;整个要求都很奇怪,而且不好。为了提供一些见解,我回答你的问题时假装这很好,但最好的答案是——根本不要依赖函数相等。只是指出,这解释了为什么你的问题是不可判定的@斯劳克的相关问题解释了其余问题。