scala foreach и инициализаторы карт - PullRequest
7 голосов
/ 11 июля 2010

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

(1 to 3) map {
  val t = 5
  i => i * 5
}


(1 to 3) foreach {  
  val line = Console.readLine  
  i => println(line)  
}  

Это какая-то документированная особенность или мне следует избегать таких конструкций? Я могу себе представить, блок «инициализации» входит в конструктор, а само замыкание становится методом apply ()?

Спасибо Пэт за оригинальный вопрос (http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline)

Ответы [ 2 ]

12 голосов
/ 11 июля 2010

Хотя используемые функции не редкость, я признаю, что это довольно странная комбинация функций.Основная хитрость заключается в том, что любой блок в Scala является выражением, тип которого совпадает с последним выражением в блоке.Если последнее выражение является функцией, это означает, что блок имеет функциональный тип и, следовательно, может использоваться в качестве аргумента для «map» или «foreach».В этих случаях происходит то, что при вызове «map» или «foreach» блок оценивается.Блок оценивается как функция (i => i * 5 в первом случае), и эта функция затем отображается в диапазоне.

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

Например, вот несколько удивительный способ вычисления первых 6 факторных чисел

(1 to 6) map {
      var total = 1
      i => {total *= i;total}
    } 

(Кстати, извините за использование факториала в качестве примера. Это был либо тот, либо Фибоначчи. Функциональное программированиеПравила гильдии. У вас проблема с этим, разберитесь с ребятами в зале.)

Менее настоятельная причина, чтобы блок возвращал функцию, - это определение вспомогательных функций ранее в блоке.Например, если бы ваш второй пример был взамен

(1 to 3) foreach {  
  def line = Console.readLine  
  i => println(line)  
}

Результатом было бы то, что три строки были прочитаны и отображены по одному разу, в то время как в вашем примере строка прочиталась один раз и повторила три раза.

1 голос
/ 11 июля 2010

Во-первых, комментарий оригинального блога " Scala Вопрос относительно readLine " упоминания поста

«line» является значением и не может быть выполнено, оноприсваивается только один раз в результате выполнения метода «Console.readLine».
Он используется менее трех раз при закрытии.
Но если вы определите его как метод, он будет выполнен трижды:

(1 to 3) foreach {
  def line = Console.readLine
  i => println(line)
}

Блог Scala для Java-беженцев. Часть 6. Переход через Java содержит интересный раздел о функции высшего порядка, в том числе:

Scalaобеспечивает еще большую гибкость в синтаксисе для этих функций-функций более высокого порядка.
В итеративном вызове мы создаем целый анонимный метод просто для повторного вызова метода println(String).
Учитывая println(String)сам по себе метод, который принимает String и возвращает Unit, можно было бы подумать, что мы можем немного сжать его.Как оказалось, мы можем:

iterate(a, println)

Опустив скобки и просто указав имя метода, мы сообщаем компилятору Scala, что хотим использовать println какфункциональное значение, передавая его методу iterate.
Таким образом, вместо того, чтобы создавать новый метод только для обработки одного набора вызовов, мы передаем старый метод, который уже делает то, что нам нужно.
Это шаблон, обычно встречающийся в C и C ++.Фактически, синтаксис для передачи функции как функционального значения точно такой же.Кажется, что некоторые вещи никогда не меняются ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...