Scala "<-" для понимания
Я обнаружил, что Scala всегда имеет "естественное объяснение" ко всему. Всегда что-то вроде "ohh, но это только функция, вызываемая этим и этим объектом с этим и этим параметром". В некотором смысле, ничто на самом деле не является компилятором, поскольку мы знаем это с других языков.
Мой вопрос касается оператора < -, который используется в следующем коде:
for(i <- 0 to 10) println(i)
В этом примере я вижу, что он переписывается на что-то вроде:
0.to(10).foreach((i:Int)=>println(i))
но это не объясняет, как i попал в анонимную функцию внутри функции foreach. В точке, где вы пишете i, это не объект и еще не объявленная переменная. Итак, что это такое и как оно переносится внутрь foreach?
Я предполагаю, что я наконец обнаружил что-то, что на самом деле является манерой компилятора
Спасибо за ваше время.
Чтобы уточнить, мой вопрос: каким образом оператор < - работает в 1-й строке кода, так как я не является объектом, на котором он может быть вызван как функция.
Ответы
Ответ 1
<-
является языковым символом ключевого слова, как и =>
, но в отличие от ->
(который является определенным символом). Поскольку он является частью основной грамматики Scala, его можно использовать для создания привязок (для i
в вашем примере), что не может быть сделано пользовательскими конструкциями.
Ответ 2
Чтобы увеличить ответ Дэйва, вот схема перевода для "понятий" из спецификации языка 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
, где., (возможно, пустая) последовательность генераторов или охранников, переведена to:
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([email protected] <- e) yield { val [email protected] = e0; (x, x0) }
Ответ 3
В этом случае, это действительно немного магии компилятора. Перевод из for-assrehension в формат filter/map/flatmap - это особый бит desugaring, подобно преобразованию специальных форм обновления и методов применения.