Соответствие с пользовательскими комбинациями / операторами - PullRequest
6 голосов
/ 18 июля 2011

Я знаю, что вы можете сопоставлять списки таким образом, как

val list = List(1,2,3)
list match {
  case head::tail => head
  case _ => //whatever
}

, поэтому я начал задумываться, как это работает.Если я правильно понимаю, :: - это просто оператор, так что мне мешает делать что-то вроде

4 match {
  case x + 2 => x //I would expect x=2 here
}

Если есть способ создать такую ​​функциональность, как это сделать;если нет, то почему?

Ответы [ 3 ]

8 голосов
/ 19 июля 2011

Сопоставление с образцом принимает входные данные и декомпозирует их с помощью функции unapply. Таким образом, в вашем случае unapply(4) должен будет вернуть два числа, сумма которых равна 4. Однако есть много пар, сумма которых равна 4, поэтому функция не будет знать, что делать.

Вам нужно, чтобы 2 был как-то доступен для функции unapply. Класс специального случая, в котором хранится 2, подойдет для этого:

case class Sum(addto: Int) {
    def unapply(i: Int) = Some(i - addto)
}

val Sum2 = Sum(2)
val Sum2(x) = 5  // x = 3

(Было бы неплохо иметь возможность сделать что-то вроде val Sum(2)(y) = 5 для компактности, но Scala не допускает параметризованные экстракторы; см. здесь .)

[РЕДАКТИРОВАТЬ: Это немного глупо, но вы могли бы также сделать следующее:

val `2 +` = Sum(2)
val `2 +`(y) = 5  // y = 3

]

РЕДАКТИРОВАТЬ: Причина, по которой работает head::tail, заключается в том, что есть только один способ отрубить голову от хвоста списка.

Нет ничего особенного в :: против +: вы можете использовать +, если у вас есть заранее определенное представление о том, как вы хотите, чтобы оно разбивало число. Например, если вы хотите, чтобы + означало «разделить пополам», то вы можете сделать что-то вроде:

object + {
    def unapply(i: Int) = Some(i-i/2, i/2)
}

и используйте его как:

scala> val a + b = 4
a: Int = 2
b: Int = 2

scala> val c + d = 5
c: Int = 3
d: Int = 2

EDIT: наконец, это объясняет, что при сопоставлении с шаблоном A op B означает то же самое, что и op(A,B), что делает синтаксис красивым.

4 голосов
/ 19 июля 2011

При сопоставлении с case head :: tail используется шаблон операции инфиксного формы p1 op p2, который переводится в op(p1, p2) перед выполнением фактического сопоставления. (См. API для ::)

Проблема с + заключается в следующем:

Хотя легко добавить

object + { 
  def unapply(value: Int): Option[(Int, Int)] = // ...
}

объект, который будет выполнять сопоставление, вы можете предоставить только один результат для каждого значения. Э.Г.

object + { 
  def unapply(value: Int): Option[(Int, Int)] = value match {
    case 0 => Some(0, 0)
    case 4 => Some(3, 1)
    case _ => None
}

Теперь это работает:

0 match { case x + 0 => x } // returns 0

также это

4 match { case x + 1 => x } // returns 3

Но это не изменится, и вы не сможете его изменить:

4 match { case x + 2 => x } // does not match

Нет проблем для ::, потому что всегда определяется, что такое head и что такое tail списка.

1 голос
/ 19 июля 2011

В Scala есть два :: s (произносится как "минусы"). Один является оператором на List s, а другой - классом, который представляет непустой список, характеризуемый головой и хвостом. Таким образом, head :: tail является шаблоном конструктора, который не имеет ничего общего с оператором.

...