Скала "<-" для понимания - PullRequest
       20

Скала "<-" для понимания

23 голосов
/ 20 сентября 2010

Я обнаружил, что у Скалы всегда есть «естественное объяснение» чему угодно.Всегда что-то вроде «ооо, но это просто функция, вызываемая для этого и того объекта с этим и этим параметром».В некотором смысле, ничто не является действительно волшебным для компилятора, поскольку мы знаем это из других языков.

Мой вопрос об операторе <- </em>, как используется в следующем коде:

for(i <- 0 to 10) println(i)

В этом примере я вижу, как он переписывается в нечто вроде:

0.to(10).foreach((i:Int)=>println(i))

, но это не объясняет, как i перенесено в анонимную функцию внутри функции foreach.В точке, где вы пишете i , это не объект и еще не объявленная переменная.Так что же это такое, и как это переносится внутрь foreach?

Я предполагаю, что наконец-то обнаружил нечто, что на самом деле магия компилятора

Спасибо за ваше время.

Чтобы уточнить, , мой вопрос: как работает оператор <- в 1-й строке кода, поскольку я не являюсь объектом, для которого его можно назватьфункция. </p>

Ответы [ 3 ]

59 голосов
/ 20 сентября 2010

Чтобы дополнить ответ Дейва, вот схема перевода «для понимания» из спецификации языка Scala:

Понимание for (enums) yield e вычисляет выражение e для каждой привязки, сгенерированной перечислителями перечисления. Последовательность перечислителя всегда начинается с генератора; за ним могут следовать другие генераторы, определения значений или охранники.

Генератор p <- e создает привязки из выражения e, которое каким-то образом сопоставляется с шаблоном p. Определение значения val p = e связывает имя значения p (или несколько имен в шаблоне p) с результатом вычисления выражения e. Страж if e содержит логическое выражение, которое ограничивает перечисляемые привязки.

Точное значение генераторов и охранников определяется переводом в вызовы из четырех методов: map, filter, flatMap и foreach. Эти методы могут быть реализованы по-разному для разных типов несущих.

Схема перевода следующая. На первом этапе каждый генератор p <- e, где p не является неопровержимым (§8.1) для типа e, заменяется на

 p <- e.filter { case p => true; case _ => false }

Затем следующие правила применяются неоднократно, пока все понимания не будут устранена.

  • За понимание for (p <- e) yield e0 переводится как e.map { case p => e0 }.

  • Понимание for (p <- e) e0 переводится как e.foreach { case p => e0 }.

  • А для понимания for (p <- e; p0 <- e0 . . .) yield e00, где. , , является (возможно, пустой) последовательностью генераторов или охранников, переводится до:
    e.flatMap { case p => for (p0 <- e0 . . .) yield e00 }.

  • А для понимания for (p <- e; p0 <- e0 . . .) e00 где. , , является (возможно, пустой) последовательностью генераторов или охранников, переводится в:
    e.foreach { case p => for (p0 <- e0 . . .) e00 }.

  • Генератор p <- e, за которым следует охрана if g переводится в один генератор:
    p <- e.filter((x1, . . . , xn) => g )
    , где x1,. , , xn - свободные переменные из p.

  • Генератор p <- e с последующим определением значения val p0 = e0 переводится следующему генератору пар значений, где x и x0 - свежие имена:

    val (p, p0) <- 
      for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }
    
18 голосов
/ 20 сентября 2010

<- - это определяемый языком символ ключевого слова, как и =>, но в отличие от -> (который является определенным символом). Поскольку он является частью базовой грамматики Scala, его можно использовать для создания привязок (для i в вашем примере), что не может быть выполнено пользовательскими конструкциями.

7 голосов
/ 20 сентября 2010

В данном случае это действительно немного волшебства компилятора. Перевод из-за понимания в форму фильтра / карты / плоской карты - это особая часть десагеринга, очень похожая на преобразование специальных форм обновления и применения методов.

...