Проблема с соответствием Scala + область - PullRequest
6 голосов
/ 30 мая 2011

Учитывая следующий код:

case class ChangeSet(field:String, from:Object, to:Object)

private var changed:List[ChangeSet] = Nil

def change(field:String, from:Object, to:Object) {
  changed.find{ case ChangeSet(field,_,_) => true } match {
    case Some(ChangeSet(field,to,_)) => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}

Строка, доставляющая мне неприятности: Some(ChangeSet(field,to,_)).

Она компилируется, но, похоже, происходит то, что Scala заполняет ее какзаполнитель для подстановочного знака.Я основываю это предположение на том факте, что когда я делаю следующее Some(ChangeSet(field,to,to)), я получаю ошибку to is already defined as value.

. Я хотел создать объект ChangeSet с to из параметров метода.

Возможно ли это?

Ответы [ 4 ]

7 голосов
/ 30 мая 2011

При сопоставлении с образцом Scala интерпретирует все идентификаторы, начинающиеся со строчных букв, как заполнители и заполняет значения. Чтобы указать Scala использовать to в качестве постоянного значения из внешней области видимости, вам необходимо заключить его в обратные черты: `to`. Кроме того, вы можете изменить имя to на To, как предложил Рекс Керр, но я предпочитаю, чтобы мои переменные были в нижнем регистре.

Это должно работать:

def change(field:String, from:Object, to:Object) {
  changed.find{ case ChangeSet(`field`,_,_) => true } match {
    case Some(ChangeSet(`field`, `to`, _)) => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}
4 голосов
/ 30 мая 2011

Кажется, здесь есть две путаницы. Первый - тот, который определили Рекс и Ким. Вы можете прочитать этот раздел из Программирование в Scala для получения дополнительной информации. Это сводится к:

x match { case Some(foo) => } // variable pattern, defines and binds variable foo
x match { case Some(Foo) => } // constant pattern, based on val Foo
x match { case Some(`foo`) => } // constant pattern for lowercase val

Вы также можете использовать охрану, чтобы ограничить совпадение

x match { case Some(foo) if condition => }

Второе заблуждение в том, что вы хотите "создать объект ChangeSet с помощью to из параметров метода" . Если я вас правильно понимаю, вы пытаетесь построить объект, используя синтаксис класса case:

ChangeSet(field, from, to)

Это не работает на той стороне сопоставления с образцом. То, что происходит на стороне case сопоставления с образцом, на самом деле можно рассматривать как reverse конструкции ChangeSet. match { case ChangeSet(field, from, to) => } сортирует ваш объект ChangeSet и присваивает его части значениям field, from и to. Это также верно, когда он составлен так: Some(ChangeSet(field, from, to)), сначала деконструирует Some, а затем ChangeSet. Вы можете видеть, что при работе с определениями значений используется тот же механизм деконструкции:

scala> val cset = ChangeSet("a", "from", "to")
cset: ChangeSet = ChangeSet(a,from,to)

scala> val Some(ChangeSet(s, o1, o2)) = Some(cset)
s: String = a
o1: java.lang.Object = from
o2: java.lang.Object = to

Похоже, вам нужно создать новый объект, который копирует значение ChangeSet, но заменяет одно поле. Классы Case поддерживают это с copy, продолжая мой пример REPL:

scala> val cset2 = cset.copy(from = o2)
cset2: ChangeSet = ChangeSet(a,to,to)

Имея это в виду, вот еще одно предложение для change:

def change(field:String, from:Object, to:Object) {
  changed.find(_.field == field) match {
    case Some(cset) =>
      val csetnew = cset.copy(from = to)
      // do stuff with csetnew
    case None =>
      // do stuff
  }
}
1 голос
/ 30 мая 2011

Если вы используете строчное имя в сопоставлении с образцом, Scala заполнит значение.Если вы хотите совпадать, только если у вас есть это значение, вам нужно использовать имя в верхнем регистре.Оставляя в стороне логику того, что вы пытаетесь сделать, и сомнительное изменение порядка имен, вы хотите:

def change(Field: String, from:Object, To: Object) {
  changed.find{
    case ChangeSet(Field,_,_) => true
    case _ => false   // You need this line!  No match is an exception, not false!
  } match {
    case Some(ChangeSet(Field,To,_)) => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}
1 голос
/ 30 мая 2011

Вы не можете использовать имена переменных в таких шаблонах. Сначала необходимо сопоставить ее с новой переменной, а затем выполнить явное сравнение.

def change(field:String, from:Object, to:Object) {
  changed.find{ case ChangeSet(f,_,_) => field == f } match {
    case Some(ChangeSet(f,too,_)) if f == field && to == too => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}
...