2015-10-07 4 views
1

Мои коллекции:Scala: Is ForComprehension Функциональный подход или императив?

val l1 = List(1, 2, 3) 
val l2 = List("A", "B", "C") 

Вот императив подход:

for { 
    e1 <- l1 
    e2 <- l2 
    } { 
    println(s"$e1 - $e2") 
    } 

И FP подход:

l1 foreach { 
    a => l2 foreach { 
    b => println { 
     s"$a - $b" 
    } 
    } 
} 

Однако классический for цикл представляет собой чисто императивный стиль, у меня есть некоторые сомнения относительно императивности цикла For Comprehension. Поэтому мой вопрос: я прав, что петля For Comprehension из императивного стиля или нет?

+1

Я не думаю, что вопрос имеет смысл. 'for (...) yield {...}' является функциональным, так как он создает новое значение. 'for (...) {}' обязательно, так как это делает побочный эффект. Оба предназначены для понимания. –

ответ

3

Для понимания - это функциональный способ написания «императивного вида» кода.

Я приведу Пол Chiusano и Runar Бьярнасон из «функционального программирования в Scala», потому что я никогда не мог поставить его лучше:

Page 89

не являются императивными и функциональными противоположностями программирования?

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

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


Пример из той же странице:

val ns: Rand[List[Int]] = 
    int.flatMap(x => 
    int.flatMap(y => 
     ints(x).map(xs => 
     xs.map(_ % y)))) 

Пока не ясно, что здесь происходит. Но так как у нас есть map и flatMap определены, мы можем использовать для постижение восстановить императивный стиль:

val ns: Rand[List[Int]] = for { 
    x <- int 
    y <- int 
    xs <- ints(x) 
} yield xs.map(_ % y) 

Этот код намного легче читать (и писать), и это выглядит что это такое - императив , который поддерживает какое-то состояние. Но это тот же код. Мы получаем следующий Int и назначить его x, получить следующий Int после этого и назначить его y, затем сгенерировать список длины x, и, наконец, вернуться в список со всеми его элементов по модулю y. Чтобы облегчить такое императивное программирование с помощью понятий (или flatMaps), нам понадобятся только два примитивных комбинатора состояний - один для чтения состояния и один для записи состояния.


Еще одна из страницы 203:

Мы можем видеть, что цепь flatMap вызовов (или эквивалентной для-понимания), как императивные программы с операторами, которые присваивают переменные , и Монада указывает, что происходит на границах инструкций.

+0

Итак, разница между функциональными/императивными стилями не отличается, но мы представляем код цикла (конкретный, в моем примере). Я прав? – Finkelson

+0

@ Finkelson. В ваших примерах ни один из них не работает, потому что они вызывают 'println', который имеет побочные эффекты. – dcastro

+0

Да, я вижу. Благодарю. – Finkelson