Кейс-классы Scala в коллекциях - PullRequest
6 голосов
/ 17 декабря 2009

Сценарий: я анализирую IL и хочу преобразовать, например, из стекового представления в CFG.

Мой IL состоит из нескольких операций, таких как PushInt (значение), Pop и т. Д. Теперь вопрос в том, какая реализация будет правильной с точки зрения Scala. Я хотел бы использовать классы / объекты case или экстракторы, чтобы я мог написать код alà

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

Теперь существует проблема с последовательностью, подобной PushInt(1) :: PushInt(1) :: Pop :: Pop, поскольку PushInt (1) равен PushInt (1), и я не могу добавить несколько (равных) операций в коллекцию. Однако я знаю, что выбрасываю некоторую информацию, которая является позицией в потоке, но это неявно сохраняется как индекс te в последовательности.

  • Одним из возможных решений является переопределение метода hashCode и нарушение правил равенства / hashCode. Я не очень доволен этим.

  • Другой вариант - иметь счетчик времени создания, который хранится в абстрактной базе, так что case class PushInt(value: Int) extends AbstractOp(AbstractOp.nextIndex)

  • Используйте экстракторы, но в этом случае я упущу хорошие функции, такие как реализация hashCode, equals, toString и, что более важно, проверку на полное совпадение.

Итак, мой вопрос в том, как смоделировать мою структуру в соответствии с моими требованиями. Является ли какое-либо из возможных решений "правильным" с точки зрения Scala?

Ответы [ 2 ]

5 голосов
/ 17 декабря 2009

Во-первых, давайте рассмотрим проблему поиска нужного вам экземпляра:

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

В любом случае, если вы повторно вставите этот экземпляр в список, у вас будут проблемы. Вы должны убедиться, что каждый экземпляр, который вы вставляете, уникален. Вы даже можете написать свою собственную коллекцию, либо сгенерировать исключение, если вставлен повторяющийся экземпляр, либо сделать копию любого экземпляра, переданного ему (достаточно просто с классами дел и методом copy в Scala 2.8).

2 голосов
/ 17 декабря 2009

Если Джоа не возражает;) Вообразите код, подобный этому:

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. Конечно, это не ответ, я не нашел, как разместить это под другими комментариями не стесняйтесь переместить его в нужное место

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...