почему фильтр должен быть определен для сопоставления с образцом в цикле for в Scala? - PullRequest
18 голосов
/ 07 декабря 2010

Чтобы создать новый класс, который можно использовать в Scala для понимания, кажется, что все, что вам нужно сделать, это определить функцию карты:

scala> class C[T](items: T*) {
     |   def map[U](f: (T) => U) = this.items.map(f)
     | }
defined class C

scala> for (x <- new C(1 -> 2, 3 -> 4)) yield x
res0: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4))

Но это работает только для простых циклов, когда в левой части <- нет сопоставления с образцом. Если вы попытаетесь сопоставить шаблон там, вы получите жалобу, что метод filter не определен:

scala> for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v
<console>:7: error: value filter is not a member of C[(Int, Int)]
       for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v

Почему для реализации сопоставления с шаблоном требуется фильтр? Я бы подумал, что Scala просто переведет вышеуказанный цикл в эквивалентный вызов map:

scala> new C(1 -> 2, 3 -> 4).map{case (k, v) => k -> v}
res2: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4))

Но это, кажется, работает нормально, поэтому цикл for должен быть переведен во что-то другое. На что это переводится, для чего нужен метод filter?

Ответы [ 2 ]

18 голосов
/ 19 мая 2012

Краткий ответ: в соответствии со спецификациями Scala вам не нужно определять метод 'filter' для примера, который вы привели, но есть open bug , что означает, что в данный момент это требуется.

Длинный ответ: алгоритм десагеринга, применяемый для понимания, описан в спецификации языка Scala . Давайте начнем с раздела 6.19 «Для пониманий и циклов» (я смотрю на версию 2.9 спецификации):

На первом этапе каждый генератор p <- e, где p не является неопровержимым (§8.1) для типа e, заменяется на p <- e.withFilter {case p => true; case _ => false}

Важным моментом для вашего вопроса является то, является ли модель в понимании «неопровержимой» для данного выражения или нет. (Шаблон - это бит перед «<-»; выражение - это бит после.) Если он «неопровержим», то withFilter не будет добавлен, в противном случае он будет необходим. </p>

Хорошо, но что значит "неопровержимый"? Перейдите к разделу 8.1.14 спецификации («Неопровержимые образцы»). Грубо говоря, если компилятор может доказать, что шаблон не может потерпеть неудачу при сопоставлении выражения, то шаблон неопровержим и вызов withFilter не будет добавлен.

Теперь ваш пример, который работает должным образом, - это первый тип неопровержимого шаблона из раздела 8.1.14, шаблон переменной. Таким образом, первый пример позволяет компилятору легко определить, что withFilter не требуется.

Ваш второй пример потенциально является третьим типом неопровержимого паттерна, паттерном конструктора. Попытка сопоставить (k, v), который равен Tuple2[Any,Any] с Tuple2[Int,Int] (см. Раздел 8.1.6 и 8.1.7 из спецификации)), успешна, поскольку Int неопровержимо для Any. Поэтому второй шаблон также неопровержим и не нуждается (не должен) в методе withFilter.

В примере Даниэля Tuple2[Any,Any] не является неопровержимым против Any, поэтому добавляются вызовы withFilter.

Кстати, в сообщении об ошибке говорится о методе filter, но в спецификации говорится о withFilter - он был изменен в Scala 2.8, см. этот вопрос и ответ для подробностей. 1035 *

12 голосов
/ 07 декабря 2010

увидеть разницу:

scala> for ((k, v) <- List(1 -> 2, 3 -> 4, 5)) yield k -> v
res22: List[(Any, Any)] = List((1,2), (3,4))

scala> List(1 -> 2, 3 -> 4, 5).map{case (k, v) => k -> v}
scala.MatchError: 5
...