Haskell #4 (An intro to lists)
이번 장에서는 하스켈의 리스트에 대해서 알아봅니다. 하스켈에서 리스트는 매우 유용하다고 합니다. 어떤 언어나 마찬가지이긴 하지만 엄청 자주 쓰는 자료구조입니다. 그만큼 중요하고 깊이 이해해야할 필요가 있다고 생각됩니다.
리스트는 같은 타입의 구성 요소를 여러개 넣을 수 있는 자료구조 입니다.
아래 예제에서 let은 GHCI에서 이름을 정의하기 위해서 사용되는 것으로 스크립트에서는 사용되지 않는 키워드입니다.
하스켈에서 리스트는 중괄호([])로 표시하고, 값들을 콤마(,)로 구분합니다. 만약 [1,2,’a’,3,’b’,’c’,4]와 같은 리스트를 만들기 위해서 시도하면 하스켈에서는 char는 숫자가 아니라는 에러를 발생 시킬 것입니다. 하스켈에서 “hello”라는 문자열은 [‘h’,’e’,’l’,’l’,’o’]라는 char의 리스트입니다.
하스켈에서는 두 리스트를 합칠 때, ++ 연산자를 사용합니다. 하스켈 내부적으로는 ++ 연산을 사용하면 ++ 왼쪽의 리스트 전체를 방문하는데, 거대한 리스트를 다루는게 아니라면 문제가 없습니다. 하지만 5000만개의 데이터를 가진 리스트의 끝에 추가하려면 시간이 필요할 것 입니다.
리스트의 앞에 넣을때는 : 연산자를 사용합니다. 이 연산자는 숫자나 숫자/문자의 리스트 또는 문자들의 리스트도 인자로 사용될 수 있습니다. 반면에 ++ 연산자는 두개의 리스트만 인자로 사용됩니다.
[1,2,3]이라는 리스트는 사실은 1:2:3:[]와 같습니다. 여기서 []은 비어있는 리스트입니다. 아래와 같은 표기는 모두 다른 리스트를 의미합니다.
- [] : 빈 리스트
- [[]] : 빈 리스트 한개를 포함하는 리스트
- [[],[],[]] : 3개의 빈 리스트를 포함하는 리스트
리스트에서 특정 인덱스의 값을 얻어올때는 !! 연산자를 사용합니다.
리스트는 리스트를 포함할 수 있습니다. 리스트안의 리스트는 다른 길이를 가질 수 있지만 다른 타입의 값을 가지고 있는 리스트들이 공존할 수는 없습니다.
리스트는 리스트에 포함된 값으로 비교가 가능합니다. <, <=, >, >= 비교 연산자를 사용해서 두 리스트의 크기를 비교할 수 있습니다. 비교할때는 사전적인 순서로 비교가되고, 앞에서부터 비교가 됩니다.
하스켈에서는 리스트를 가지고 놀 수 있는 아래와 같은 기본 함수들을 제공합니다.
- head : 리스트의 첫번째 값을 가져옵니다.
- tail : 리스트의 첫번째 값을 제외한 나머지 값들의 리스트를 가져옵니다.
- last : 리스트의 마지막 값을 가져옵니다.
- init : 리스트의 마지막 값을 제외한 나머지 값들의 리스트를 가져옵니다.
리스트를 몬스터에 비유하면 아래 그림처럼 표현될 수 있습니다.
빈 리스트에는 head, tail, last, init을 사용할 수 없습니다. 이 에러는 컴파일 타임에 잡을 수 없기때문에 위와같은 에러를 대비하는 것이 좋습니다.
length로 리스트의 길이를 얻을 수 있습니다.
null을 사용하여 리스트가 비어있는지 체크할 수 있습니다. null 대신 xs == [] (xs가 리스트 이름이라면..)를 사용해서 체크해도 됩니다.
reverse로 리스트를 뒤집을 수 있습니다.
take로 리스트에서 주어진 개수 만큼만 리스트를 가져올 수 있습니다. 리스트의 크기보다 큰 개수를 요구하면 리스트 전체를 읽어오고, 0을 요구하면 빈 리스트를 반환하는 것을 확인할 수 있습니다.
drop으로 리스트의 앞에서부터 주어진 개수만큼 값을 버릴 수 있습니다.
minimum, mazimum을 사용하여 리스트에서 최소값, 최대값을 가져올 수 있습니다.
sum으로 모든 리스트내 값의 합을 구할 수 있고, product로 모든 값을 곱을 구할 수 있습니다.
elem은 주어진 값이 리스트에 존재하는 값인지를 체크할 수 있습니다. elem은 일반적으로 가독성을 위해 infix 함수(인자를 양옆에 받는 방식)로 사용됩니다.
여기까지 리스트의 기초적인 함수들을 살펴보았습니다. 더 많은 함수는 이후에 다루게 됩니다.