List 모듈

List 모듈

연결 리스트(linked list)는 선택한 순서대로 요소가 0개, 1개, 혹은 그 이상 들어 있는 자료구조예요. Elixir의 리스트는 대괄호로 표현해요.

iex> [1, "two", 3, :four]
[1, "two", 3, :four]

두 리스트는 ++/2--/2 연산자로 연결하거나 뺄 수 있어요.

iex> [1, 2, 3] ++ [4, 5, 6]
[1, 2, 3, 4, 5, 6]
iex> [1, true, 2, false, 3, true] -- [true, false]
[1, 2, 3, true]

| 연산자로 리스트 앞에 요소를 추가할 수 있어요.

iex> new = 0
iex> list = [1, 2, 3]
iex> [new | list]
[0, 1, 2, 3]

출처: List

본문

Elixir의 리스트는 사실상 연결 리스트예요. 내부적으로 리스트의 head와 tail을 담은 쌍으로 표현돼요.

iex> [head | tail] = [1, 2, 3]
iex> head
1
iex> tail
[2, 3]

이런 쌍(cons cell)만으로 리스트 [1, 2, 3]을 쓸 수도 있어요.

iex> [1 | [2 | [3 | []]]]
[1, 2, 3]

마지막 cons cell의 두 번째 요소가 빈 리스트가 아닌 리스트를 improper list라고 불러요.

iex> [1 | [2 | [3 | 4]]]
[1, 2, 3 | 4]

improper list는 보통 피하지만, iodata나 chardata와 같은 특별한 상황에서 쓰여요(IO 모듈 참고).

cons cell 기반 표현 덕분에 리스트 앞쪽에 요소를 추가하는 것은 항상 빠르고(상수 시간), 뒤에 붙이는 것은 리스트가 커질수록 느려져요(선형 시간).

iex> list = [1, 2, 3]
iex> [0 | list]    # 빠름
[0, 1, 2, 3]
iex> list ++ [4]   # 느림
[1, 2, 3, 4]

이 모듈의 대부분 함수는 선형 시간으로 동작해요. 즉 연산 시간이 리스트 길이와 같은 비율로 늘어나요. length/1last/1은 모든 요소를 순회해야 하므로 선형이고, first/1은 첫 요소만 필요하므로 상수 시간이에요.

리스트는 Enumerable 프로토콜도 구현하므로 리스트를 다루는 많은 함수가 Enum 모듈에 있어요. 또한 다음 함수·연산자는 Kernel에서 찾을 수 있어요.

  • ++/2
  • --/2
  • hd/1
  • tl/1
  • in/2
  • length/1

Charlists

리스트가 음이 아닌 정수로 이루어졌고 각 정수가 유니코드 코드 포인트를 나타낸다면, 그 리스트를 charlist라고도 불러요. 이 정수들은 다음 조건을 만족해야 해요.

  • 0..0x10FFFF(0..1_114_111) 범위 안에 있어야 하고,
  • 0xD800..0xDFFF(서러게이트) 범위 밖이어야 해요.

이렇게 하면 charlist는 바이너리(비트열)로 변환될 수 있고, 또 그 반대로도 가능해요. charlist에 대한 자세한 내용은 charlist에 대한 글을 참고하세요.

주요 함수

first/1, last/1, at/3

first(list)
last(list)
at(list, index, default \ nil)

first/1은 첫 요소를, last/1은 마지막 요소를 돌려줍니다. at/3은 0부터 시작하는 index 위치의 요소를 돌려주고, 범위를 벗어나면 default를 돌려줘요.

flatten/1

flatten(list)

중첩 리스트를 평평하게 펼쳐요.

iex> List.flatten([1, [[2], 3]])
[1, 2, 3]

foldl/3, foldr/3

foldl(list, acc, fun)
foldr(list, acc, fun)

왼쪽부터(foldl), 또는 오른쪽부터(foldr) 함수를 접어 누적값을 만들어요. foldl은 tail-recursive라서 큰 리스트를 다룰 때 스택을 아끼는 데 좋아요.

insert_at/3, delete_at/2, replace_at/3, update_at/3, pop_at/2

insert_at(list, index, value)
delete_at(list, index)
replace_at(list, index, value)
update_at(list, index, fun)
pop_at(list, index, default \ nil)

인덱스 기반의 삽입·삭제·교체·업데이트·추출 함수예요. 0부터 시작하고 음수 인덱스는 뒤에서부터 세요.

keyfind/4, keymember?/3, keysort/2, keyreplace/4, keydelete/3, keytake/3

튜플 리스트에서 키(주어진 위치의 요소)를 기준으로 검색·정렬·교체·삭제·추출하는 함수들이에요. 예를 들어 keyfind/4는 주어진 키와 일치하는 첫 튜플을 돌려줍니다.

iex> List.keyfind([{:a, 1}, {:b, 2}], :b, 0)
{:b, 2}

wrap/1, unwrap/1, to_atom/1, to_charlist/1, to_integer/1, to_string/1

wrap/1은 값이 리스트가 아니면 리스트로 감싸고, unwrap/1은 원소가 하나뿐인 리스트를 풀어줘요. List.to_atom/1·to_integer/1·to_string/1·to_charlist/1처럼 문자열 변환 계열도 제공돼요.

duplicates?/1

duplicates?(list)

리스트에 중복 요소가 있는지 검사해요.

더 알아보기

  • Enum 모듈: 리스트를 포함한 열거 가능한 구조를 다루는 함수
  • Kernel 모듈: ++/2, --/2, hd/1 등 연산자·함수
  • charlist 타입 문서: 유니코드 코드 포인트 리스트