Обработка списка

Обработка списка — процесс получения из терма-списка другого терма, часто тоже списка.

Список отличается следующими особенностями:

У кортежей обычно заранее известно количество внутренних элементов, поэтому их легко анализировать с помощью паттернов. В том плане, что паттерн сопоставляется с кортежем целиком. Для анализа списков паттерны тоже активно применяются, но они сопоставляются с отдельными элементами списка, а не списком целиком. Списки могут содержать тысячи и миллионы элементов, тут паттернами для списка целиком не запасёшься.

Тем не менее, конечно, Эрланг позволяет сопоставлять список целиком с паттерном.

1> ['раз', X, 'три'] = ['раз', 'два', 'три']. 
['раз','два','три']
2> X.
'два'

Эффективным способом обработки списков является обработчик списка.

Для обработки списка — так, как нам нужно — можно (и часто нужно) написать свою функцию. Но сначала очень желательно посмотреть, нет ли уже готовой библиотечной функции для этих целей. Если таковая есть, надо пользоваться ею, потому что она, очень вероятно, будет работать быстрее (многие пишутся на Си). В идеале неплохо было бы помнить всё содержимое модуля lists. Но работа со списками может быть и в других библиотечных модулях, например string.

Функции модуля lists

all(Pred, List) -> boolean() Возвращает true, если каждый элемент списка удовлетворяет предикату.

any(Pred, List) -> boolean() Возвращает true, если хотя бы один элемент списка удовлетворяет предикату.

append(ListOfLists) -> List1 Берёт сложный терм (список списков) и упрощает его: каждый элемент входит в новый единый список.

append(List1, List2) -> List3 Объединяет два списка в один.

concat(Things) -> string() Превращает “вещи” в их текстовые представления и конкатенирует эти строки в одну. Things это список из элементов, каждый из которых: атом, строка, целое или вещественное число.

delete(Elem, List1) -> List2 Удаляет первый попавшийся элемент, который соответствует Elem из списка List1.

droplast(List) -> InitList Удаляет последний элемент из списка.

dropwhile(Pred, List1) -> List2 Каждый элемент проверяет предикатом. Сначала отбрасывает все элементы. Как только найдётся элемент, соответствующий предикату, с этого момента все элементы списка возвращаются.

duplicate(N, Elem) -> List Делает список из N элементов Elem.

enumerate(List1) -> List2 enumerate(Index, List1) -> List2 enumerate(Index, Step, List1) -> List2 Нумеруют список, то бишь каждый элемент превращают в кортеж. Пример:

1> lists:enumerate(['Атос', 'Портос', 'Арамис']). 
[{1,'Атос'},{2,'Портос'},{3,'Арамис'}]

filter(Pred, List1) -> List2 Оставляет в списке только те элементы, которые удовлетворяют предикату.

filtermap(Fun, List1) -> List2 Фильтрует список с помощью фунтерма. Если фунтерм возвращает true или {true, Value}, элемент сохраняется.

flatlength(DeepList) -> integer() >= 0 Эквивалентно length(flatten(DeepList)), но более эффективно.

flatmap(Fun, List1) -> List2 С помощью фунтерма обрабатывается каждый элемент списка. Фунтерм должен возвращать список. Элементы этого списка добавляются в итоговый список. Таким образом, фунтерм на вход получает элемент, а возвращает несколько элементов. Пример:

1> lists:flatmap(fun(X) -> [X,X,X,X] end, [a,b,c]). 
[a,a,a,a,b,b,b,b,c,c,c,c]

flatten(DeepList) -> List flatten(DeepList, Tail) -> List Берёт глубокий список и делает из него плоский. Примеры:

1> lists:flatten([[],[],[[],[[],[],"hello"]],[],[]]).
"hello"
2> lists:flatten([[],[],[[],[[],[],"hello"]],[],[]], " world").
"hello world"

foldl(Fun, Acc0, List) -> Acc1 Обрабатывает список с помощью фунтерма и аккумулятора. Фунтерм получает на вход очередной элемент и текущее значение аккумулятора. Acc0 — значение аккумулятора в самом начале. В итоге функция возвращает последний аккумулятор.

foldr(Fun, Acc0, List) -> Acc1 То же, что и foldl/3, но список обрабатывается справа налево.

foreach(Fun, List) -> ok Вызывает на каждом элементе фунтерм. В итоге возвращает лишь атом ok. Используется для разного рода побочных эффектов. Например, чтобы вывести на печать каждый элемент.

join(Sep, List1) -> List2 Разбавляет список сепараторами: между всеми соседними элементами исходного списка добавляет элемент-сепаратор. Так можно, например, строку разбавить символами.

1> lists:join(32, "hello").
"h e l l o"

keydelete(Key, N, TupleList1) -> TupleList2 В списке, который состоит из кортежей, берётся каждый элемент (каждый кортеж). Если N-й элемент кортежа равен Key, этот кортеж пропускается и не попадает в итоговый список. Пример:

1> TL = lists:enumerate(['Атос', 'Портос', 'Арамис']).
[{1,'Атос'},{2,'Портос'},{3,'Арамис'}]
2> lists:keydelete(3,1,TL). 
[{1,'Атос'},{2,'Портос'}]

keyfind(Key, N, TupleList) -> Tuple | false Ищет в списке кортежей кортеж, чей N-й элемент равен Key. Возвращает этот кортеж, если таковой найден. Иначе — false.

keymap(Fun, N, TupleList1) -> TupleList2 Берет список кортежей. В каждом кортеже берёт N-й элемент и пропускает его через фунтерм Fun. Итоговый список кортежей содержит изменённые элементы.

keymember(Key, N, TupleList) -> boolean() Берёт список кортежей, в каждом сравнивает N-й элемент с Key. Если найдётся таковой кортеж, функция возвращает true.

keymerge(N, TupleList1, TupleList2) -> TupleList3 Объединяет два списка кортежей. Предполагается, что эти списки уже отсортированы по ключу N в кортежах. Новый список кортежей получается тоже отсортированным по этому ключу. Пример:

1> TL1 = [{'бананы',6},{'мандарины',8},{'яблоки',4}].
[{'бананы',6},{'мандарины',8},{'яблоки',4}]
2> TL2 = [{'арбузы',1},{'персики',14},{'хурма',2}]. 
[{'арбузы',1},{'персики',14},{'хурма',2}]
3> lists:keymerge(1,TL1,TL2). 
[{'арбузы',1},
{'бананы',6},
{'мандарины',8},
{'персики',14},
{'хурма',2},
{'яблоки',4}]

keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 Возвращает копию списка кортежей — за одним исключением. Первый кортеж, в котором N-й элемент равен Key, будет заменён на NewTuple.

keysearch(Key, N, TupleList) -> {value, Tuple} | false Ищет в списке кортежей тот, чей N-й элемент равен Key. Эта функция оставлена для обратной совместимости. Обычно keyfind/3 удобнее.

keysort(N, TupleList1) -> TupleList2 Сортирует список кортежей по ключу N.

keystore(Key, N, TupleList1, NewTuple) -> TupleList2 В списке кортежей ищет тот, у которого N-й элемент равен Key. Если таковой имеется, заменяет его на NewTuple. Если такового нет, дописывает NewTuple в конец списка кортежей.

keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} | false В списке кортежей ищет тот, у которого N-й элемент равен Key. При наличии такового возвращает {value, Tuple, TupleList2}, где Tuple — найденный кортеж, а TupleList2 — новый список кортежей, из которого был изъят Tuple (только один, первый найденный).

last(List) -> Last Возвращает последний элемент списка.

map(Fun, List1) -> List2 Каждый элемент списка пропускает через фунтерм. Из новых значений формируется новый список — с таким же количеством элементов.

mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} Комбинация map/2 и foldl/3 — за один проход. То есть и пропускает через фунтерм каждый элемент списка, и при этом делает свёртку, возвращая аккумулятор. Действует слева направо.

mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} Как mapfoldl/3, только справа налево.

max(List) -> Max Возвращает наибольший элемент списка.

member(Elem, List) -> boolean() Проверяет, есть ли такой элемент в списке.

merge(ListOfLists) -> List1 merge(List1, List2) -> List3 merge(Fun, List1, List2) -> List3 merge3(List1, List2, List3) -> List4 Возвращает объединённый отсортированный список.

min(List) -> Min Возвращает минимальный элемент.

nth(N, List) -> Elem Возвращает N-й элемент списка.

nthtail(N, List) -> Tail Возвращает хвост списка — то, что после N-го элемента.

partition(Pred, List) -> {Satisfying, NotSatisfying} Список с помощью предиката делится на два. Элементы первого удовлетворяют предикату, элементы второго — нет.

prefix(List1, List2) -> boolean() Если List1 является префиксом списка List2, будет true.

reverse(List1) -> List2 Возвращает список в обратном порядке.

reverse(List1, Tail) -> List2 Возвращает развернутый в другую сторону список вместе с добавленным хвостом.

search(Pred, List) -> {value, Value} | false Если какое-то значение (элемент) списка удовлетворяет предикату, возвращается в кортеже с пометкой value.

seq(From, To) -> Seq seq(From, To, Incr) -> Seq Возвращает список — последовательность целых чисел от From до To. Если указан Incr, то с этим шагом.

sort(List1) -> List2 sort(Fun, List1) -> List2 Сортирует элементы. Можно указать сортирующий фунтерм, который принимает два аргумента, A и B. Если A<=B, фунтерм должен вернуть true. Иначе — false.

split(N, List1) -> {List2, List3} Разделяет список на два. List2 содержит первый N элементов. Остальные достаются List3.

splitwith(Pred, List) -> {List1, List2} Делит список на две части с помощью предиката. Пока элементы удовлетворяют предикату, они попадают в первый список. Все остальные (включая те, которые тоже удовлетворяют предикату) попадают во второй список.

sublist(List1, Len) -> List2 sublist(List1, Start, Len) -> List2 Возвращает часть списка. Если указать Start, то с этой позиции, иначе с 1-й. Len — длина подсписка. Если она окажется слишком большой, это не будет ошибка, просто вернётся всё до конца исходного списка.

subtract(List1, List2) -> List3 Перебирает элементы из List2. Для каждого из них ищет первое появление в List1 и удаляет его. Пример:

1> lists:subtract("abcdabcabc", "cac"). 
"bdababc"

suffix(List1, List2) -> boolean() Если List1 является суффиксом у List2, возвращается true. Иначе false. Пример:

1> lists:suffix("щик", "Паромщик").
true

sum(List) -> number() Возвращает сумму элементов (которые числа).

takewhile(Pred, List1) -> List2 Возвращает префикс — все первые элементы, которые удовлетворяют предикату.

ukeymerge(N, TupleList1, TupleList2) -> TupleList3 Объединяет два списка отсортированных кортежей в третий — тоже отсортированный. Сортировка осуществляется по N-му элементу. Подразумевается, что оба исходных списка кортежей уже отсортированы по N-му элементу.

ukeysort(N, TupleList1) -> TupleList2 Сортирует список кортежей по N-му элементу, при этом удаляя дубли.

umerge(ListOfLists) -> List1 umerge(List1, List2) -> List3 umerge(Fun, List1, List2) -> List3 umerge3(List1, List2, List3) -> List4 Возвращает отсортированное объединение списков.

uniq(List1) -> List2 uniq(Fun, List1) -> List2 Удаляет дубли, сохраняя порядок элементов. Из одинаковых элементов сохраняется первый.

unzip(List1) -> {List2, List3} unzip3(List1) -> {List2, List3, List4} “Расстёгивает” список кортежей, каждый из которых состоит из двух (или трёх) элементов. Из первых элементов образуется List2, из вторых — List3.

usort(List1) -> List2 usort(Fun, List1) -> List2 Возвращает список, состоящий из отсортированных уникальных элементов.

zip(List1, List2) -> List3 zip(List1, List2, How) -> List3 zip3(List1, List2, List3) -> List4 zip3(List1, List2, List3, How) -> List4 “Застёгивает” два (или три) списка в список кортежей, каждый из которых состоит из двух (или трёх) элементов. Первые элементы кортежей берутся из первого списка. Вторые — из второго. Атрибут How определяет, как себя ведёт “застёгивание” в нестандартных случаях.

zipwith(Combine, List1, List2) -> List3 zipwith(Combine, List1, List2, How) -> List3 zipwith3(Combine, List1, List2, List3) -> List4 zipwith3(Combine, List1, List2, List3, How) -> List4 Комбинирует элементы из двух (или трёх) списков в один с помощью фунтерма Combine.

Документация

Модуль lists — официальная документация.


© Алексей Карманов, 2024.