之前在scala-user邮件列表中询问了此问题,但没有确认答案。
scala> val T = new Pair(1, 2){
override def equals(obj:Any) = obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1}
}
T: (Int, Int) = (1,2)
scala> T match {
case (1, 1) => println("matched")
case _ => println("not matched")
}
not matched
scala> (1, 1) match {
case T => println("matched")
case _ => println("not matched")
}
not matched
scala> T == (1, 1)
res15: Boolean = true
我认为常量(val)模式匹配结果取决于“等于”的返回值,但结果表明情况并非如此,那么标准是什么?
有人建议case (1, 1) =>
是提取器模式,而是使用Tuple2.unapply
。所以我尝试了这些:
scala> Pair.unapply(T)
res1: Option[(Int, Int)] = Some((1,2))
scala> Pair.unapply(T).get == (1, 1)
res2: Boolean = true
任何人都可以解释为什么==
成真,但我不能让它们匹配?
答案 0 :(得分:13)
您的示例的问题是您只覆盖您定义特定元组的匿名类的equals
方法。让我们仔细看看你在运行这里给出的代码时正在做什么。
val p = new Pair(1, 2) {
override def equals(obj:Any) = {
obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
}
}
Scala在这里做的是创建一个新的匿名类,它扩展Pair
并覆盖它的等号。所以这相当于运行以下代码:
class Foo extends Pair(1,2) {
override def equals(obj:Any) = {
obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
}
}
val p = new Foo
在这里,您可以确切地看到问题所在! equals
的定义不对称。 p == (1,1)
评估为true
,(1,1) == p
评估为false
!这是因为前者等同于p.equals((1,1))
,而后者等同于(1,1).equals(p)
。在您给出的示例中,它不起作用,因为案例中的对象与匹配的对象进行比较,而不是相反。因此,如您所示,Pair.unapply(p).get == (1, 1)
评估为true
,但(1,1) == Pair.unapply(p).get
评估为false
,而后者似乎是匹配时使用的。{/ p >
但是,在任何情况下,创建一个非对称的等号是一个非常糟糕的想法,因为代码的执行取决于你比较对象的顺序。此外,等于你定义的还有一个问题 - 当您尝试将p
与 类型为Pair
的任何(Int, Int)
进行比较时,它会失败并显示错误。这是因为,在类型擦除(这是JVM如何实现泛型)之后,Pair
不再被其成分的类型参数化。因此,(Int, Int)
与(String, String)
具有完全相同的类型,因此,以下代码将失败并显示错误:
p == ("foo", "bar")
因为Scala会尝试将(String, String)
投射到(Int, Int)
。
如果你想实现这个功能,你可以做的最简单的事情是使用pimp我的库模式,拉伸Pair
。但是,您不应该调用方法equals
。称之为其他内容,例如~=
。我现在必须要去,但是当我回来时,我可以给你代码。这很容易。您应该查看对中equals
的实现,并删除比较第二个参数的部分:)
答案 1 :(得分:3)
我不得不说Scala再一次让我感到惊讶。更多测试显示结果取决于运行上下文。
如果你跑:
object Pair{
def main(args:Array[String]) {
val T = new Pair(1, 2){
override def equals(obj:Any)= obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
}
(1, 1) match {
case T => println("matched")
case _ => println("not matched")
}
}
}
你得到:匹配
如果你跑:
object Pair extends Application {
val T = new Pair(1, 2){
override def equals(obj:Any)= obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
}
(1, 1) match {
case T => println("matched")
case _ => println("not matched")
}
}
你得到:不匹配
我一直认为main()方法中的代码与扩展Application trait的对象体中的代码没有区别。真的很奇怪。
答案 2 :(得分:2)
元组在模式匹配器中具有特权。他们不是你的日常课程。这是规范,第8.1.7节“元组模式”。
当你说
时(1, 1) match { case T ...
然后在(1,1)上调用equals,这当然表示不,谢谢,不等于。
当你说
时T match { case (1, 1) => ...
然后由于元组模式而忽略了你的equals方法,并且T._1被比较为1而T._2被比较为1,并且它再次不匹配。
答案 3 :(得分:2)
在#3888的分辨率下,我可以对这个问题给出最终答案。
T match { case (1, 1) =>
不,它与unapply
无关。正如临时提到的那样,case (1,1) =>
是一个'Tuple Pattern',是案例类Tuple2的'Constructor Pattern'的别名,它只匹配构造为Tuple2(1,1)或Pair(1,1)的值。
真正关心unapply
的是'Extractor Pattern':
object Pair {
val T = new Pair(1,1){
def unapply(p:(Int, Int)) :Boolean = this._1 == p._1
}
def main(args: Array[String]) = {
(1, 2) match {
case T() => println("matched")
case _ => println("not matched")
}
}
}
你得到“匹配”。请注意case子句中的()
(1, 1) match { case T => ...
根据scala规范第8.1.5节,这是一个'稳定标识符模式',case T
匹配任何值v,使得T == v。所以我们应该得到“匹配”。 “不匹配”的结果
只是由当前编译器实现中的错误引起的。