리스트는 하스켈에서 가장 범용적으로 사용되는 데이터 타입입니다. 리스트의 타입은[a]
로 표시하는데, a
는 요소 타입을 뜻합니다. 예를 들어 Char
의 리스트는 [Char]
,Int
의 리스트는 [Int]
와 같이 표시합니다.
리스트 [a]
는 머리(head)와 꼬리(tail)로 구성되고 머리는 타입 a
, 꼬리는 또 다른 리스트 [a]
타입을 가집니다. 구성 요소에 자기 자신의 타입을 반복되기 때문에 리스트와 같은 타입을 재귀 타입(recursive type)이라고 부릅니다.
머리와 꼬리를 이용하여 이용하여 리스트를 만드는 연산자는 (:)
입니다. []
는 아무 요소도 없는 빈 리스트를 의미합니다. 리스트를 새로 만들려면 일단 리스트가 하나 있어야 하기 때문에 맨 처음에는 빈 리스트를 이용합니다. 1
과 []
을 합하면 리스트 [1]
이 됩니다.
> 1 : []
[1]
> 1 : 2 : []
[1,2]
> 1 : 2 : 3: []
[1,2,3]
여기서 (:)
연산자는 우결합(right-assocative)하기 때문에 1:2:3:[]
의 의미는(1:(2:(3:[])))
과 같습니다. 먼저 3
과 []
를 합하여 [3]
을 만들고 다시 여기에 2
를 합하여 [2,3]
를 만들고 다시 여기에 1
을 합하여 [1,2,3]
을 만든다고 이해하시면 됩니다.
매번 이렇게 리스트를 생성하면 불편하기 때문에 조금 더 쉽게 리스트를 생성하는 방법이 있습니다. []
안에 ,
로 구분하여 요소들을 나열해주면 됩니다.
> [False, True, False]
[False,True,False]
> [1,2,3]
[1,2,3]
> []
[]
length
함수는 리스트의 요소 개수를 리턴합니다.
> length [1, 2, 3]
3
> length [1]
1
> length []
0
참고로 우리가 쓰고 있는 String
타입은 실제로는 Char
타입의 리스트인 [Char]
로 정의되어 있습니다. 따라서 리스트를 인자로 받는 함수인 length
에 String
을 줘도 문자열의 길이를 리턴합니다.
> length "abc"
3
> length ""
0
head
함수는 리스트의 머리를 돌려줍니다. 빈 리스트를 인자로 받았을 경우에는 돌려줄 머리가 없으므로 에러가 납니다.
> head [1, 2, 3]
1
> head []
*** Exception: Prelude.head: empty list
반대로 tail
함수는 리스트의 꼬리를 돌려줍니다. 마찬가지로 빈 리스트를 인자로 받았을 때는 돌려줄 꼬리가 없으므로 에러가 납니다.
> tail [1, 2, 3]
[2,3]
> tail []
*** Exception: Prelude.tail: empty list
(++)
연산자는 리스트 두 개를 합친 새로운 리스트를 리턴합니다.
> [1, 2, 3] ++ [4, 5, 6]
[1,2,3,4,5,6]
> [1, 2, 3] ++ []
[1,2,3]
reverse
함수는 리스트의 순서를 거꾸로 뒤짚어서 리턴합니다. 빈 리스트는 뒤짚을 요소가 없으므로 그대로 빈 리스트를 리턴합니다.
> reverse [1, 2, 3]
[3,2,1]
> reverse []
[]
sum
이나 product
함수를 이용하면 리스트 요소들의 모두 더한 값이나 모두 곱한 값을 얻을 수도 있습니다.
> sum [1, 2, 3, 4, 5]
15
> product [1, 2, 3, 4, 5]
120
take
나 drop
함수를 이용하면 서브 리스트를 얻을 수 있습니다. take
는 주어진 개수만큼의 요소를 새로운 리스트로 만들어 리턴합니다. drop
은 주어진 개수만큼의 요소를 빼거 나머지 요소들을 새로운 리스트로 만들어 리턴합니다.
> take 3 [1, 2, 3, 4, 5]
[1,2,3]
> drop 3 [1, 2, 3, 4, 5]
[4,5]
이 외에도 Data.List 문서를 보시면 리스트에 정의된 수많은 함수들을 확인하실 수 있습니다. 리스트는 매우 자주 사용되는 데이타 타입이므로 리스트 함수들은 여러 번 공부하고 익혀서 숙지하고 계시면 좋습니다.
Pingback: 하스켈 리스트 기초 – 서광열의 하스켈 스쿨