集合中的Scala案例类

时间:2009-12-16 21:25:31

标签: scala

场景:我正在解析IL并且想要从基于堆栈的表示转换为CFG。

我的IL包含多个操作,如PushInt(值),Pop等。问题是现在哪个实现在Scala方面是正确的。我很乐意使用案例类/对象或提取器,以便我可以编写代码alà

op match {
  case PushInt(x) => doSomethingWith x
  case Pop => ...
}

现在问题存在像PushInt(1) :: PushInt(1) :: Pop :: Pop这样的序列,因为PushInt(1)等于PushInt(1),我无法将多个(相等)操作添加到集合中。但是,我知道我正在抛出一些信息,这是流程中的位置,但这被隐含地存储为序列中的te索引。

  • 一种可能的解决方案是覆盖hashCode方法并破坏equal / hashCode的规则。我对此并不满意。

  • 另一种选择是拥有一个存储在抽象基础中的“创建时间”计数器,以便case class PushInt(value: Int) extends AbstractOp(AbstractOp.nextIndex)

  • 使用提取器,但在这种情况下,我会错过很好的功能,例如hashCode,equals,toString的实现,更重要的是检查详尽的匹配。

所以现在我的问题是如何根据我的要求对我的结构进行建模。在Scala方面,任何可能的解决方案都是“正确的”吗?

2 个答案:

答案 0 :(得分:5)

首先,让我们解决找到你想要的确切实例的问题:

scala> trait AbstractOp
defined trait AbstractOp

scala> case class Pop() extends AbstractOp {
     |   override def equals(other: Any) = other match {
     |     case that: Pop => this eq that
     |     case _ => false
     |   }
     | }
defined class Pop

scala> case class PushInt(val i: Int) extends AbstractOp {
     |   override def equals(other: Any) = other match {
     |     case that: PushInt => this eq that
     |     case _ => false
     |   }
     | }
defined class PushInt

scala> val l = List(PushInt(1), PushInt(1), Pop(), Pop())
l: List[Product with AbstractOp] = List(PushInt(1), PushInt(1), Pop(), Pop())

scala> val op = l(1)
op: Product with AbstractOp = PushInt(1)

scala> println( l.indexOf( op ) )
1

当然,这意味着PushInt(1) != PushInt(1),除非它是PushInt(1)的完全相同的实例。它不会因equals而违反hashCode / a.equals(b) => a.hashCode == b.hashCode合同,但a.hashCode == b.hashCode并不意味着什么。但是,如果您唯一的用途是查找该实例,请尝试以下方法:

scala> case class Pop() extends AbstractOp
defined class Pop

scala> case class PushInt(val i: Int) extends AbstractOp
defined class PushInt

scala> val l = List(PushInt(1), PushInt(1), Pop(), Pop())
l: List[Product with AbstractOp] = List(PushInt(1), PushInt(1), Pop(), Pop())

scala> val op = l(1)
op: Product with AbstractOp = PushInt(1)

scala> println( l.findIndexOf( op eq _ ) )
1

无论哪种方式,如果您在列表中重新插入该实例,您将遇到麻烦。您必须确保插入的每个实例都是唯一的。您甚至可以编写自己的集合,如果插入重复的实例则抛出异常,或者传递给它的任何实例的副本(在Scala 2.8上使用case类和copy方法很容易)。

答案 1 :(得分:2)

如果Joa不介意;)想象一下这样的代码:

trait AbstractOp
case class Pop() extends AbstractOp
case class PushInt(val i:Int) extends AbstractOp

现在我们构建一个表示程序指令序列的列表

val l=List(PushInt(1), PushInt(1), Pop(), Pop())

第一个问题:您想要获取操作的索引

val op=l(1) // get the second operation for example
// now you want to get back the index for the op you are using
println( l.indexOf( op1 ) ) // you will get 0 and not 1

第二个问题:您希望将上一个列表中的每个操作映射到一个值,这将失败,因为equals不会区分两个Pop或两个PushInt。

P.S。 当然这不是一个答案,我还没有找到如何在其他评论下发布这个 随意将它移动到正确的位置