Scala 空列表等式是如何工作的?
操作员是否真的按内容比较列表?特别是关于空列表 以下比较工作与预期一样Scala 空列表等式是如何工作的?,scala,types,equals,equality,subtyping,Scala,Types,Equals,Equality,Subtyping,操作员是否真的按内容比较列表?特别是关于空列表 以下比较工作与预期一样 List("A", "B", "C") == "ABC".split("").toList // true List() == List() // true List.empty[String] == List.empty[String] // true 但是,不同类型的空列表比较会产生令人困惑的结果: List.empty[String] == List.empty[Int] // true: on different
List("A", "B", "C") == "ABC".split("").toList // true
List() == List() // true
List.empty[String] == List.empty[String] // true
但是,不同类型的空列表比较会产生令人困惑的结果:
List.empty[String] == List.empty[Int] // true: on different types?
编辑:在最初的问题中,我做了一个误导性的测试用例,Andrey对此进行了澄清。谢谢复制于此
val emptyStrSplit = "".split("").toList // List("") and not List() as displayed in Console
List.empty[String] == emptyStrSplit // false: b/c List() != List("")
是单例对象List.empty[String]
,它扩展了Nil
(协变地,是List[Nothing]
的子类型)List[String]
是单例对象List.empty[Int]
,它扩展了Nil
(协变地,是List[Nothing]
的子类型)List[Int]
- 每个单例对象都等于它自己
- 因此,
给出trueNil==Nil
Nil
,它同时是List[String]
类型和List[Int]
类型。这就是你得到的,如果你有子类型。我看不出有什么奇怪或矛盾的地方
如果要确保类型相同,可以使用默认值null
的implicit
类型证据A=:=B
,然后检查编译器是否提供了非null
证据:
def eqSameType[A, B](a: A, b: B)(implicit ev: A =:= B = null) =
if (ev == null) false else a == b
例如:
scala> eqSameType(List.empty[Int], List.empty[String])
res4: Boolean = false
再加上Andrey的回答,即使
List.empty[T]
(或List[T]()
)确实返回了List
的一个新实例,由于类型擦除,您仍然应该期望各种类型的空列表是相等的。例如,以ListBuffer
为例,其empty
方法每次都返回一个新的ListBuffer
:
import scala.collection.mutable.ListBuffer
ListBuffer.empty[Int] == ListBuffer.empty[String]
如果希望找到一种方法来检测两个列表何时具有不同的编译时类型,可以使用:
不过,我不确定这什么时候有用。编辑:使用
ClassTag
的这个实现还不够好。Brian使用TypeTag
的答案更好
虽然从Scala的角度来看,Andrey的回答很有道理。我觉得将List.empty[String]==List.empty[Int]
设置为false而不是true更符合商业意义。下面是一个自定义比较器,它使用上下文绑定来支持以下内容。不知道这是不是最优雅的方式
import scala.reflect.{ClassTag, classTag}
def customCompareLists[T1: ClassTag, T2: ClassTag](l1: List[T1], l2: List[T2]): Boolean = {
classTag[T1] == classTag[T2] && l1 == l2
}
customCompareLists(List(), List()) // true
customCompareLists(List.empty[Double], List.empty[Double]) // true
customCompareLists(List.empty[String], List.empty[Int]) // false
customCompareLists(List(1,2), List("A")) // false
customCompareLists(List(1,2), List(1,2)) // true
// FAILED on this case
customCompareLists(List.empty[List[String]], List.empty[List[Int]]) // true: INCORRECT
即使忽略类型擦除问题和“它们实际上是同一个对象”问题,
equals
onSeq
的文档说明:
def等于(即:任意):布尔值
任意序列的equals方法
返回:如果该序列的元素与该序列的元素顺序相同,则返回true,否则返回false
这使得所有空序列相等:它们具有相同顺序的相同元素,即无。空的列表
等于空的向量
、队列
、流
等
您可能还对提供类型安全平等性的库感兴趣,例如,等等。“”.split(“”)。toList
不是List()
。它是列表(“”)
。在你消除了错误的假设后,这个问题还有什么重要的问题吗?拆分(“”
是空的?toString
很难理解,因为列表(“”
中的空字符串在打印输出中是不可见的,所以看起来像列表()
。嗨,输出列表()愚弄了oups
的。拆分(“”)。toList
无法检查大小,谢谢。所以这个问题相当愚蠢。现在唯一有趣的部分是如何检查两个不同类型的空列表List.empty[T1]==List.empty[T2]
。现在我明白为什么了。谢谢你的澄清。但仍处于业务逻辑级别。希望List.empty[String]==List.empty[Int]
生成false
,尽管使用定制的比较器。见完整答案below@Polymerase不,这不一定是“可取的”。一些人认为,如果不同类型的x
和y
根本不进行打字检查,则更可取:。甚至更好!还通过了Brian的测试用例eqSameType(List.empty[List[String]],List.empty[List[Int]]//false
同一天有这么多好事:-)。调用的语法是什么?我想进一步阅读文档。@Polymerase=:=
是一个二进制中缀类型的构造函数(sugar for=:=[a,B]
),=null
是a=:=B
类型的参数ev
的默认值。我认为=:=
东西被称为“广义类型约束”。哦,酷,看起来我们在几秒钟内就想出了相同的想法。顺便说一句,你能详细说明一下TypeTag
vsClassTag
的用法吗?请参见文档链接。TypeTag有更多关于泛型的信息,因此基于classtag的解决方案将在嵌套更深入的泛型上失败。考虑<代码>均衡器(列表):空[列表[Str] ],列表,空[列表[INT] ] < /代码>;这实际上是一个你的答案失败的极端情况。我怀疑“它更有商业意义”取决于你的用例。
import scala.reflect.{ClassTag, classTag}
def customCompareLists[T1: ClassTag, T2: ClassTag](l1: List[T1], l2: List[T2]): Boolean = {
classTag[T1] == classTag[T2] && l1 == l2
}
customCompareLists(List(), List()) // true
customCompareLists(List.empty[Double], List.empty[Double]) // true
customCompareLists(List.empty[String], List.empty[Int]) // false
customCompareLists(List(1,2), List("A")) // false
customCompareLists(List(1,2), List(1,2)) // true
// FAILED on this case
customCompareLists(List.empty[List[String]], List.empty[List[Int]]) // true: INCORRECT