Скала: как избежать мутации?
Это обычное дело, когда нужно накапливать данные. То, как я привык к этому, - это добавление фрагментов данных в массив. Но это плохая практика в скале, так как я могу избежать этого?
3 ответа
Ну, есть два общих способа борьбы с накоплением: рекурсия и складывание. Давайте рассмотрим очень простые примеры каждого из них, чтобы вычислить сумму значений списка.
def sumRecursively(list: List[Int]): Int = {
def recurse(list: List[Int], acc: Int): Int =
if (list.isEmpty) acc
else recurse(list.tail, acc + list.head)
recurse(list, 0)
}
def sumFolding(list: List[Int]): Int =
list.foldLeft(0){ case (acc, n) => acc + n }
Есть много вариантов этого, которые лучше обрабатывают тот или иной случай.
В большинстве случаев операции "map" и "flatMap" используются для функциональной генерации структур данных. Оба начинают с одной структуры данных, применяют некоторую операцию к каждому элементу в ней и возвращают новую структуру данных той же формы, что и оригинал. Они отличаются только тем, как заполняется новая структура данных. Эти два настолько распространены и настолько мощны, что в Scala есть специальный синтаксис - для понимания - для их поддержки. Внешнее понимание выглядит внешне похожим на цикл for в стиле Java, но фактически компилируется в серию вызовов map и flatMap (среди нескольких других).
В функциональном программировании обычно разбивают вашу проблему на преобразования, подобные этим, из одной структуры данных в другую, а не в явном виде описывают шаги, необходимые для создания и уничтожения ваших структур данных. Это требует некоторого привыкания, особенно при определении структуры данных, с которой нужно начинать. Как только вы овладеете им, это чрезвычайно мощная техника, позволяющая четко, точно и четко выражать большие куски функциональности, и при этом остается мало места для ошибок.
Также стоит отметить, что "map" и "flatMap" на самом деле являются частными случаями другой, более мощной функции: "fold". "fold" (реализованный как "foldLeft" и "foldRight" по техническим причинам) может использоваться как для построения структур данных, так и для их разбивки.
На самом деле это не так. Вы можете использовать Vector
в скале, которая является частью scala.collection.immutable
пакет по умолчанию. Это создаст неизменную коллекцию, которая будет возвращать новый (другой) экземпляр каждый раз, когда вы добавляете его.
Дополнительная информация:
http://www.scala-lang.org/docu/files/collections-api/collections_15.html