Скала "<-" для понимания
Я обнаружил, что у Скалы всегда есть "естественное объяснение" чему угодно. Всегда что-то вроде "ооо, но это просто функция, вызываемая для этого и того объекта с этим и этим параметром". В некотором смысле, ничто не является действительно волшебством компилятора, поскольку мы знаем это от других языков.
Мой вопрос об операторе <-, который используется в следующем коде:
for(i <- 0 to 10) println(i)
В этом примере я вижу переписывание чего-то вроде:
0.to(10).foreach((i:Int)=>println(i))
но это не объясняет, как я попал в анонимную функцию внутри функции foreach. В точке, где вы пишете i, это не объект и еще не объявленная переменная. Так что же это такое и как оно переносится внутрь foreach?
Я думаю, что я наконец-то обнаружил нечто, что на самом деле является магией компилятора
Спасибо за ваше время.
Чтобы уточнить, у меня вопрос: как работает оператор <- в 1-й строке кода, поскольку я не являюсь объектом, для которого он может быть вызван как функция.
3 ответа
<-
является определяемым языком символом ключевого слова, как =>
но в отличие от ->
(который является определенным символом). Поскольку он является частью базовой грамматики Scala, его можно использовать для создания привязок (для i
в вашем примере), что не может быть сделано с помощью пользовательских конструкций.
Чтобы дополнить ответ Дейва, вот схема перевода "для понимания" из спецификации языка Scala:
Понимание
for (enums) yield e
оценивает выражениеe
для каждой привязки, сгенерированной перечислителями. Последовательность перечислителя всегда начинается с генератора; за этим могут следовать другие генераторы, определения значений или защита.Генератор
p <- e
производит привязки из выраженияe
который каким-то образом сопоставляется с шаблономp
, Определение значенияval p = e
связывает имя значенияp
(или несколько имен в шаблонеp
) к результату оценки выраженияe
, Охранникif e
содержит логическое выражение, которое ограничивает перечисляемые привязки.Точное значение генераторов и охранников определяется переводом в вызовы четырех методов:
map
,filter
,flatMap
, а такжеforeach
, Эти методы могут быть реализованы по-разному для разных типов несущих.Схема перевода выглядит следующим образом. На первом этапе каждый генератор
p <- e
где р не является неопровержимым (§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) }
В этом случае это действительно немного волшебства компилятора. Перевод из-за понимания в форму фильтра / карты / плоской карты - это особая часть десагеринга, очень похожая на преобразование специальных форм обновления и применения методов.