갈루아의 반서재

728x90

하스켈에서 리스트는 동질(homogenous)의 데이터 구조이다. 리스트는 같은 타입의 원소를 가진다. 이 말은 정수의 리스트 또는 문자 리스트 등은 가질 수 있지만, 정수 일부, 문자 일부로 구성되는 리스트는 존재하지 않는다는 의미이다. 

* let 키워드를 이용하여 GHCI 에서 이름을 정의할 수 있다. GHCI 에서 let a = 1 이라고 하는 것은 스크립트에 a = 1 이라고 쓰고 로딩하는 것과 동일하다. 

  1. Prelude> let lostNumbers = [4,8,15,16,23,42]
  2. Prelude> lostNumbers
  3. [4,8,15,16,23,42]
  4. Prelude>

리스트는 대괄호로 표현되고고, 리스트의 값들은 콤마로 구분되어진다. 

문자에 관해서 이야기하면 문자열은 단지 문자의 리스트라고 보면 된다. 즉, "hello" 는 ['h','e','l','l','o'] 의 리스트인 셈이다.  문자열이 리스트인 관계로, 그에 대해서도 간단하게 리스트 함수를 응용할 수 있다. ++ 연산자를 이용해서 2개의 리스트를 결합하는 것이 가장 기본적인 예이다. 

  1. Prelude> [1,2,3,4] ++ [9,10,11,12]
  2. [1,2,3,4,9,10,11,12]
  3. Prelude> "hello" ++ " " ++ "world"
  4. "hello world"
  5. Prelude> ['w','o'] ++ ['o','t']
  6. "woot"

++ 연산자를 통해 리스트를 결합할 때 내부적으로 하스켈은 ++ 왼쪽의 리스트 전체를 순회한다. 리스트가 크지 않을 때는 문제가 되지 않지만, 규모가 클 때는 상당히 시간이 걸릴 수 밖에 없다. 이에 반해 : 연산자(이른바 cons operator)의 경우 결과처리는 즉각적이다. 다음의 예를 보자. 

  1. Prelude> 'A':" SMALL CAT"
  2. "A SMALL CAT"
  3. Prelude> 5:[1,2,3,4,5]
  4. [5,1,2,3,4,5]

[1,2,3] 는 사실 1:2:3:[] 의 syntactic sugar 일 뿐이다. [] 는 비어있는 리스트이다. 여기에 3을 덧붙이면 [3]이 된다. 거기에 2를 덧붙이면 [2,3] 이 된다. 이런 식이다. 

그리고 아래 셋은 서로 다른 것이다. 

[] - 비어있는 리스트

[[]] - 비어있는 리스트를 포함하고 있는 리스트

[[],[],[]] - 3개의 비어있는 리스트를 포함하고 있는 리스트


인덱스를 이용하여 리스트내의 원소를 가져올 때는 !! 을 사용한다. 인덱스는 0 에서 시작한다. 하지만 6번째 원소를 가져오려고 하면, 에러가 발생한다. 

  1. Prelude> "Steve Buscemi" !! 6
  2. 'B'
  3. Prelude> [9.4,33.2,96.2,11.2,23.25] !! 1
  4. 33.2
  5. Prelude> [9.4,33.2,96.2,11.2,23.25] !! 6
  6. *** Exception: Prelude.!!: index too large
  7. Prelude>


리스트 역시 리스트를 포함할 수 있다. 그리고 리스트를 포함한 리스트를 포함한 리스트도 가능하다. 

  1. Prelude> let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
  2. Prelude> b
  3. [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
  4. Prelude> b ++ [[1,1,1,1]]
  5. [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]
  6. Prelude> [6,6,6]:b
  7. [[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
  8. Prelude> b !! 2
  9. [1,2,2,3,4]

숫자와 문자가 섞인 리스트를 만들 수 없듯이, 문자로 이루어진 리스트와 숫자로 이루어진 리스트를 모두 포함하는 리스트는 만들 수 없다(즉, 서로 다른 타입의 리스트를 하나의 리스트에 포함할 수 없다).

포함된 원소들이 비교가능하다면 리스트 역시 비교가능하다. <, <=, > 그리고 >= 를 리스트를 비교할 때 사용할 때는 사전적 순서(lexicographical order)로 비교된다. 먼저 헤드가 비교되면, 그것이 동일하다면 그 다음 원소가 서로 비교되는 것이다. 

  1. Prelude> [3,2,1] > [2,1,0]
  2. True
  3. Prelude> [3,2,1] > [2,10,100]
  4. True
  5. Prelude> [3,4,2] > [3,4]
  6. True
  7. Prelude> [3,4,2] > [2,4]
  8. True
  9. Prelude> [3,4,2] == [3,4,2]
  10. True

head 명령어는 리스트를 가져와 그 리스트의 헤드를 반환한다. 리스트의 헤드는 기본적으로 리스트의 첫번째 원소를 의미한다. tail은 헤드를 제외한 나머지를 반환한다. last 는 리스트의 가장 마지막 원소를 반환한다. 마지막으로 init는 마지막 원소를 제외한 나머지를 반환한다.

  1. Prelude> head [5,4,3,2,1]
  2. 5
  3. Prelude> tail [5,4,3,2,1]
  4. [4,3,2,1]
  5. Prelude> last [5,4,3,2,1]
  6. 1
  7. Prelude> init [5,4,3,2,1]
  8. [5,4,3,2]

만약 빈 리스트에 헤드를 적용하면 어떻게 될까? 아래에서 보듯이 head, tail, last, init 명령은 빈 리스트에는 적용할 수 없다.

  1. Prelude> head[]
  2. *** Exception: Prelude.head: empty list
  3. Prelude> last[]
  4. *** Exception: Prelude.last: empty list
  5. Prelude> tail[]
  6. *** Exception: Prelude.tail: empty list
  7. Prelude> init[]
  8. *** Exception: Prelude.init: empty list
  9. Prelude>


728x90