Какими правилами следует руководствоваться подчеркиванием для определения анонимной функции?

Я использую _ как заполнитель для создания анонимной функции, и проблема в том, что я не могу предсказать, как Scala собирается преобразовать мой код. Точнее, он по ошибке определяет, насколько "большую" анонимную функцию я хочу.

 List(1,2,3) foreach println(_:Int)   //error !
 List(1,2,3) foreach (println(_:Int)) //work
 List(1,2,3) foreach(println(_:Int))  //work

С помощью -Xprint:typer Я вижу, что Scala превращает первый в "большую анонимную функцию":

x$1 => List(1,2,3) foreach(println(x$1:Int))

сработавшие 2-е 3-е - это правильное превращение в то, что я хочу.

... foreach (x$1 => println(x$1:Int)) 

Почему это? Какое правило?

2 ответа

Решение

Простые правила для определения области подчеркивания:

  1. Если подчеркивание является аргументом метода, тогда область действия будет вне этого метода, в противном случае соответствующие правилам ниже;
  2. Если подчеркивание находится внутри выражения, ограниченного () или {}, будет использован самый внутренний такой разделитель, который содержит подчеркивание;
  3. При прочих равных условиях будет использовано максимально возможное выражение.

Итак, по правилу № 1 вместо println((x: Int) => x), сфера будет размещена снаружи (в том числе) println,

По правилу № 2 последние два примера будут иметь функцию, разделенную скобками, поэтому (x => println(x: Int)),

По правилу № 3, первым примером будет целое выражение, так как нет разделительных скобок.

Я считаю, что ответ мистера Собрала неверен. Действительные правила можно найти в справочнике по языку Scala, раздел 6.23, подзаголовок "Синтаксис заполнителя для анонимных функций".

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

К сожалению, мое объяснение поведения, которое наблюдал г-н Ласковский для своего первого примера, немного сложное и умозрительное. когда

List(1,2,3) foreach println(_:Int)

набирается в цикле чтения-оценки-печати Scala. Сообщение об ошибке:

error: type mismatch;
 found   : Unit
 required: Int => ?
              List(1,2,3) foreach println(_:Int)
                                         ^

Если вы измените пример чуть-чуть:

List(1,2,3).foreach println(_:Int)

сообщение об ошибке легче понять -

error: missing arguments for method foreach in class List;
follow this method with `_' if you want to treat it as a partially applied function
          List(1,2,3).foreach println(_:Int)
                      ^

Чтобы понять вещи немного лучше, звоните scala таким образом: scala -Xprint:parser, который после того, как пользователь вводит каждое выражение, вызывает печать выражения, выделенного синтаксическим анализатором. (Наряду с большим количеством мусора, который я опущу.) Для первого примера Laskowski выражение, понятное парсеру:

((x$1: Int) => List(1, 2, 3).foreach(println((x$1: Int))))

Для второго примера версия парсера

((x$1: Int) => List(1, 2, 3).foreach.println((x$1: Int)))

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

Другие вопросы по тегам