场景:我正在解析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方面,任何可能的解决方案都是“正确的”吗?
答案 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。 当然这不是一个答案,我还没有找到如何在其他评论下发布这个 随意将它移动到正确的位置