- '평범한 한글'은 여러 개의 데이터를 한꺼번에 묶어서 다를 수 있는 목록(list)을 제공한다.
- 목록이 있으면 반복해서 계산해야 할 내용을 정리하기가 편리하다.
- Python 의
list
, JavaScript 의Array
와 굉장히 닮아있다.
목록 만들기 - ㅁㄹ
- 목록을 만들려면
ㅁㄹ
함수를 사용한다. ㅁㄹ
: 목록. 인수의 개수 만큼 객체를 받아서 목록을 내놓는다.[요소1] [요소2] [요소3]....(요소n] ㅁㄹ ㅎ[요소 개수]
ㄴ ㄷ ㄹ ㅁㄹ ㅎㄹ
[1, 2, 3]
- 출력 결과로
[1, 2, 3]
이 나왔는데, 여기서[ ]
는 일반적으로 목록을 나타내는 기호이다. 하지만 '평범한 한글'은 한글이 아닌 모든 기호를 무시하기 때문에 목록을 만들 때도[ ]
기호를 쓰지 않는다. 출력할 때만 사용되는 기호이다.
- 1, 2, 3 세 개의 인수를 사용하여
ㅁㄹ
함수를 호출(ㅎㄹ
)하면 세 개의 실수를 담은 목록을 내놓는다.
- 목록에 들어 있는 자료를 요소(element)라고 한다.
[1, 2, 3]
은 세 개의 요소를 가진 목록이다.
ㅈㅈ ㅎㄱ ㄱㅈ ㅎㄱ ㅁㄹ ㅎㄷ
[True, False]
- 논릿값을 목록에 담을 수도 있다.
ㅈㅈ ㅎㄱ
는 참(True)을,ㄱㅈ ㅎㄱ
는 거짓(False)을 내놓고, 두 개의 인수로ㅁㄹ
을 호출하여 목록을 만든다.
- '평범한 한글'의 목록은 서로 다른 자료형의 요소를 담을 수 있다. C와 같은 일반적인 명령형 언어의 배열(array)과는 다른 특징이다.
ㄴ ㅈㅈㅎㄱ ㄷ ㄱㅈㅎㄱ ㄹ ㅁㄹ ㅎㅂ
[1, True, 2, False, 3]
- 위의 목록에는 세 개의 실수와 두 개의 논릿값이 들어 있다.
- 이처럼 서로 다른 자료형을 요소로 갖는 목록을 '이종 목록'(heterogeneous list)이라고 한다.
- 반면, 같은 자료형만 담을 수 있는 Haskell 의 리스트는 '동종 목록'(homogeneous list)이다.
- 목록에는 '평범한 한글'에서 제공하는 모든 종류의 객체를 다 넣을 수 있다.
ㄹ ㅎ ㅂㄱ ㅎㄱ ㄱㄴㄱ ㅁㅈ ㅎㄴ ㅂ ㅅ ㅈ ㅁㄹㅎㄹ ㅁㄹ ㅎㅁ
[<Closure created at depth 0>, Nil, '8', [5, 6, 7]]
ㄹ ㅎ
는 3을 내놓는 함수,ㅂㄱ ㅎㄱ
는 빈 값(Nil),ㄱㄴㄱ ㅁㅈ ㅎㄴ
는 문자열 '8'이다.
- 당연히 목록 안에 또 다른 목록이 들어갈 수 있다.
ㅂ ㅅ ㅈ ㅁㄹㅎㄹ
는 [5, 6, 7]이다.
- 코드의 가독성을 높이려면 다음과 같이 기호를 사용할 수 있다.
[(ㄹ ㅎ), (ㅂㄱ ㅎㄱ), (ㄱㄴㄱ ㅁㅈ ㅎㄴ), [ㅂ ㅅ ㅈ ㅁㄹㅎㄹ]] ㅁㄹ ㅎㅁ
- 위의 코드는 어디까지나 보기일 뿐이므로 자기가 알아보기 편하게 기호를 사용해도 상관 없다.
('평범한 한글'이 난해한 언어인데 굳이 이렇게 코드를 정리할 필요가.... 있다... 있어.)
- 아무 요소도 없는 빈 목록을 만들 수도 있다.
ㅁㄹ ㅎㄱ
[]
- 아무 인수 없이
ㅁㄹ
을 호출하면 빈 목록을 내놓는다.
TMI: 빈 목록[], 빈 값(Nil)
프로그램을 짜다 보면 빈 목록(empty list)과 빈 값(Nil)이 헷갈릴 때가 종종 있다. 이 때 위의 '짤'을 보면 뭔가 무릎을 탁 치게 된다. 왼쪽이 빈 목록이고 오른쪽이 빈 값이다.
빈 목록은 목록 자체는 존재하지만 요소가 없는 것이고, 빈 값은 값 자체가 아예 존재하지 않는 상태이다. 그래서 빈 값은 프로그램에서 '정상적이지 않는 상태'를 나타낼 때 주로 쓰인다.
언어에 따라 빈 값을 나타내는 방식도
null
, nil
, Nil
, none
, None
, Nothing
처럼 다양하다. 모두 정상적인 상태가 아니라 뭔가 잘못된 상태를 나타낸다.빈 목록에 해당하는 다른 자료형으로는 실수나 숫자에서의 0이 있고, 문자열에서는 빈 문자열(
""
)이 있다. 분명히 이들과 빈 값(Nil)은 달리 구분해야 한다. '평범한 한글'에도 빈 값과 빈 목록을 구분하고 있으며 서로 다른 의미로 사용된다.길이도 재고 더하기도 하고... - ㅈㄷ, ㄷ
- 목록 안에 몇 개의 요소가 들어있는지 확인하려면
ㅈㄷ
함수를 사용한다. ㅈㄷ
. 인수로 주어진 목록의 길이를 구한다. 즉 몇 개의 요소가 들어 있는지 구하여 내놓는다. ('길이를 재다'로 외우자)[목록] ㅈㄷ ㅎㄴ
- 지금까지 우리가 만든 목록의 길이를 재 보자.
ㄴ ㄷ ㄹ ㅁㄹ ㅎㄹ ㅈㄷ ㅎㄴ ㅈㅈ ㅎㄱ ㄱㅈ ㅎㄱ ㅁㄹ ㅎㄷ ㅈㄷ ㅎㄴ ㄴ ㅈㅈ ㅎㄱ ㄷ ㄱㅈ ㅎㄱ ㄹ ㅁㄹ ㅎㅂ ㅈㄷ ㅎㄴ ㄹ ㅎ ㅂㄱ ㅎㄱ ㄱㄴㄱ ㅁㅈ ㅎㄴ ㅂ ㅅ ㅈ ㅁㄹㅎㄹ ㅁㄹ ㅎㅁ ㅈㄷ ㅎㄴ ㅁㄹ ㅎㄱ ㅈㄷ ㅎㄴ
3 2 5 4 0
- 마지막의 빈 목록(
ㅁㄹ ㅎㄱ
)은 아무 요소도 없기 때문에 길이가 0이다.
ㄷ
으로 숫자를 더하는 것처럼 목록을 더할 수도 있다.ㄷ
: 더하기. 여러 개의 목록을 더하여 하나의 목록을 내놓는다.
ㄴ ㄹ ㅂ ㅁㄹㅎㄹ ㄷ ㅁ ㅁㄹㅎㄷ ㄷㅎㄷ
[1, 3, 5, 2, 4]
- 한 개의 목록에 다른 목록을 더할 때는 항상 이전 목록의 끝부터 더한다.
ㄴ ㅁㄹㅎㄴ ㄷ ㄹ ㅁㄹㅎㄷ ㅁ ㅂ ㅅ ㅁㄹㅎㄹ ㄷ ㅎㄹ
[1, 2, 3, 4, 5, 6]
[1]
,[2, 3]
,[4, 5, 6
] 세 개의 목록을 더하면 당연히[1, 2, 3, 4, 5, 6]
이 된다. 몇 개의 목록을 더하더라도 하나의 목록은 이전 목록의 제일 끝에 붙는다.
- 당연한 것이지만 여러 개의 목록을 더할 때에는 더하려는 인수의 개수를 잘 따져서 함수를 호출해야 한다. 위에서는 세 개의 목록을 더하기 때문에
ㄷ ㅎㄹ
를 사용했다.
- 다음 코드의 실행 결과는 무엇일까?
ㄴ ㄷ ㄹ ㅁㄹㅎㄹ ㅁ ㅂ ㅁㄹㅎㄷ ㅁㄹㅎㄷ ㄴㄱ ㄷㄱ ㄹㄱ ㅁㄹㅎㄹ ㄷ ㅎㄷ
① [[1, 2, 3], [4, 5], [-1, -2, -3]]
② [[1, 2, 3], [4, 5], -1, -2, -3]
③ [1, 2, 3, 4, 5, -1, -2, -3]
정답
②번. 목록 안에 다시 목록이 있다고 해도, 더하려는 요소는 실수 객체이기 때문.
만일 ①처럼 만들고 싶다면 다음과 같이 해야 한다.
ㄴ ㄷ ㄹ ㅁㄹㅎㄹ ㅁ ㅂ ㅁㄹㅎㄷ ㅁㄹㅎㄷ ㄴㄱ ㄷㄱ ㄹㄱ ㅁㄹㅎㄹ ㅁㄹㅎㄴ ㄷ ㅎㄷ
- 목록을 더할 때 빈 목록은 결과에 아무런 영향도 주지 않는다.
ㅁㄹㅎㄱ ㄴ ㅁㄹㅎㄴ ㄷ ㄹ ㅁㄹㅎㄷ ㅁ ㅂ ㅅ ㅁㄹㅎㄹ ㅁㄹㅎㄱ ㄷ ㅎㅂ
[1, 2, 3, 4, 5, 6]
- 빈 목록은 여러 개를 아무리 더해도 빈 목록이다.
ㅁㄹㅎㄱ ㅁㄹㅎㄱ ㅁㄹㅎㄱ ㅁㄹㅎㄱ ㅁㄹㅎㄱ ㄷㅎㅂ
[]
- 빈 목록 다섯 개를 더해도 결과는 빈 목록이다.
0 + 0 + 0 + 0 + 0 = 0
과 같은 이치.
번호로 목록 요소 가져오기
- 목록에 있는 요소는 위치에 따라 번호(index)를 갖고 있다. 첫번째 요소는 0, 두번째는 1, 세번째는 2... 이런 식이다. 일반적인 프로그래밍 언어에서 쓰는 방식이다.
- 목록에서 특정 번호의 요소에 접근하려면 다음과 같이 한다
[요소 번호] [목록] ㅎㄴ
요소 번호
가 0 이상 이면 해당하는 위치의 요소에 접근한다.요소 번호
가 음수이면 뒤에서부터 세어서 접근한다. 맨 뒤의 요소는 -1이다.
ㄱ ㅁ ㅂ ㅅ ㅈ ㅁㄹㅎㅁ ㅎㄴ ㄴㄱ ㅁ ㅂ ㅅ ㅈ ㅁㄹㅎㅁ ㅎㄴ
4 7
ㅁ ㅂ ㅅ ㅈ ㅁㄹㅎㅁ
은 [4, 5, 6, 7] 목록을 만든다.
- 이 목록에 각각
0
과-1
을 인수로 넣어서 호출(ㅎㄴ
)하면 4와 7을 내놓는다. 0은 첫 번째 요소 번호이고, -1은 맨 뒤의 요소 번호이다.
- 이렇게 보면 목록을 마치 함수 처럼 호술하는 모양세이다. 논릿값을 호출해서 조건 판단을 하는 것과 비슷한 원리로 볼 수 있다.
- 만약에 목록의 길이보다 더 큰 요소 번호로 호출하면 어떻게 될까?
ㄷㄴㄱ ㅁ ㅂ ㅅ ㅈ ㅁㄹㅎㅁ ㅎㄴ
문제가 생겼습니다. EvalError: Unexpected value: undefined
undeinfed
는 정의되지 않은 값, 즉 의미가 없는 값이라는 뜻이다.
- 목록의 길이보다 더 큰 요소 번호를 사용하면 안 되는 것은 너무나 당연한 일이지만, 대놓고 이렇게 틀리는 경우 보다 버그 때문에 요소 번호 계산에 오류가 생겨서 발생하는 때가 더 많다.
부분 목록 발췌하기 - ㅂㅈ
- 목록에서 두 개 이상의 요소를 가져와서 새로운 목록을 만들려면
ㅂㅈ
함수를 사용한다. ㅂㅈ
. 발췌(slice). 목록 하나와 실수 세 개를 받아서 목록이 가진 요소를 발췌하여 새로운 목록을 내놓는다.[목록] [처음 위치] [포함하지 않을 위치] [건너뛸 값] ㅂㅈ ㅎ[인수 개수)]
- 목록: 발췌할 목록
- 처음 위치: 발췌를 시작할 위치. 음수를 지정하면 끝에서 부터 센다.
- 포함하지 않을 위치: 발췌를 마치고 포함하지 않을 위치. 생략하면 목록의 끝까지 발췌. 음수를 지정하면 끝에서 부터 센다.
- 발췌할 단위: 요소를 발췌할 때 단위를 정한다. 생략하면 건너뛰지 않고 모든 요소를 발췌.
ㄴ ㄷ ㄹ ㄴㄱ ㄷㄱ ㄹㄱ ㅁㄹㅎㅅ ㄱ ㄹ ㅂㅈㅎㄹ ㄴ ㄷ ㄹ ㄴㄱ ㄷㄱ ㄹㄱ ㅁㄹㅎㅅ ㄹㄱ ㅂㅈㅎㄷ ㄴ ㄷ ㄹ ㄴㄱ ㄷㄱ ㄹㄱ ㅁㄹㅎㅅ ㄱ ㅅ ㄹ ㅂㅈㅎㅁ
[1, 2, 3] [-1, -2, -3] [1, -1]
- [1, 2, 3, -1, -2, -3] 목록에서 0번째 요소부터 3번째 요소 전까지 발췌하면 [1, 2, 3] 목록이 된다.
- '처음 위치'와 '포함하지 않을 위치'를 지정해 주었다.
- [1, 2, 3, -1, -2, -3] 목록의 -3번째, 즉 끝에서 베 번째 요소에서 끝까지 발췌하면 [1, 2, 3]이 된다.
- '처음 위치'만 지정해 주었다. 포함하지 않을 위치를 생략하면 목록의 맨 끝 요소가 된다.
- [1, 2, 3, -1, -2, -3] 목록의 0번째(처음)부터 6번째 전(5번째) 요소까지 발췌하되, 3 개 단위로 발췌하면 5번째와 3번째 요소가 발췌된다. [1, -1] 목록이 만들어진다.
- '처음 위치', '포함하지 않을 위치', '발췌할 단위'가 모두 지정되었다.
요소 마다 함수 적용(map) - ㅁㄷ
- 목록이 유용한 것은 여러 개의 데이터를 한 데 묵어서 다룰 수 있고, 함수를 이용해서 얼마든지 마음대로 가공할 수 있기 때문이다.
- 목록에 있는 요소를 가공해서 새로운 목록을 만들어 낼 수 있다. 함수형 언어의 중요한 구성 요소 중 하나인 map 이다.
ㅁㄷ
: 요소 '마다' 적용. 목록 하나와 함수 하나를 받아서 요소마다 함수를 적용하여 새 목록을 내놓는다.[목록] [함수] ㅁㄷ ㅎㄷ
- 목록: 적용할 요소들이 담긴 목록
- 함수: 요소 마다 적용할 함수. 1개의 인수를 갖는다.
ㄴ ㄷ ㄹ ㅁ ㅂ ㅁㄹㅎㅂ ㄱㅇㄱ ㄷ ㅅㅎㄷ ㅎ ㅁㄷ ㅎㄷ
[1, 4, 9, 16, 25]
ㅁㄷ
함수는 두 개의 인수를 받는다.- 여기서는 [1, 2, 3, 4, 5] 목록과
- 첫 번째 인수(
ㄱㅇㄱ
)를 제곱하는(ㄷ ㅅㅎㄷ
) 함수(ㅎ
)이다.
ㅁㄷ
함수는 목록의 각 요소에ㄱㅇㄱ ㄷ ㅅㅎㄷ ㅎ
를 적용하면, 이 함수의ㄱㅇㄱ
는 목록의 각 요소가 된다. 이를 이용해서 함수에서 필요한 가공을 하기만 하면ㅁㄷ
함수가 이를 모아서 새로운 목록을 내놓는다.
ㄴ ㄹ ㅂ ㅈ ㄷ ㅁ ㅅ ㅁㄹㅎㅈ ㄱㅇㄱ ㅁㅈㅎㄴ ㅎ ㅁㄷㅎㄷ
['1', '3', '5', '7', '2', '4', '6']
- 주어진 목록에 있는 요소를 문자열로 바꾼 목록을 만든다. 주어진 인수를 문자열로 바꾸는 함수(
ㄱㅇㄱ ㅁㅈㅎㄴ ㅎ
)가 열심히 일을 하면ㅁㄷ
함수는 결괏값을 잘 갈무리하여 새로운 목록을 만들어 내놓는다.
ㄱ ㅁㄹㅎㄴ ㄱ ㄴ ㄷ ㅁㄹㅎㄹ ㄱ ㄴ ㄷ ㄹ ㅁ ㅁㄹㅎㅂ ㄱ ㄴ ㄷ ㄹ ㅁ ㅂ ㅅ ㅁㄹㅎㅈ ㅁㄹㅎㅁ ㄱㅇㄱ ㅈㄷㅎㄴ ㅎ ㅁㄷㅎㄷ
[1, 3, 5, 7]
- 네 개의 목록을 만들고 이 목록의 목록을(!) 만든다. 목록에는 '평범한 한글'의 어떤 자료형을 가진 요소도 다 넣을 수 있다는 점을 기억하면 목록 안에 목록을 담을 수 있다는 건 당연한 일이다.
- 네 개의 목록이 담긴 목록에 각 요소의 길이를 구하는 함수(
ㄱㅇㄱ ㅈㄷㅎㄴ ㅎ
)를 전달하여ㅁㄷㅎㄷ
를 호출하면 각 목록의 길이를 구해서 [1, 3, 5, 7]을 내놓는다.
조건에 따라 선별(filter) - ㅅㅂ
- 목록에 있는 요소들 중 조건에 맞는 것들만 선별해서 새로운 목록을 만들 수 있다. 함수형 언어에서 map과 함께 중요하게 다루어지는 filter이다.
ㅅㅂ
: 선별. 목록 하나와 함수 하나를 받아서 조건에 맞는 요소만 뽑아내서 새로운 목록을 만든다.[목록] [조건 함수] ㅅㅂ ㅎㄷ
- 목록: 조건을 검사할 목록
- 조건 함수: 요소 하나하나를 판별해서 논릿값(True, False)을 내놓는 함수.
ㄴ ㄹ ㅂ ㅈ ㄷ ㅁ ㅅ ㅁㄹㅎㅈ ㄱㅇㄱ ㅂ ㅈㅎㄷ ㅎ ㅅㅂ ㅎㄷ
[1, 3, 2, 4]
- [1, 3, 5, 7, 2, 4, 6] 목록에서 5보다 작은지 판별하는 함수(
ㄱㅇㄱ ㅂ ㅈㅎㄷ ㅎ
)를 이용해서 목록을 걸러주면 [1, 3, 2, 4]로 이루어진 목록이 된다.
ㄴ ㄹ ㅂ ㅈ ㄷ ㅁ ㅅ ㅁㄹㅎㅈ ㅂ ㄱㅇㄱ ㅈㅎㄷ ㅁㅎㄱ ㅎ ㅅㅂ ㅎㄷ
[7, 6]
- [1, 3, 5, 7, 2, 4, 6] 목록에서 5보다 큰 요소를 선별하려면 조건으로
ㅂ ㄱㅇㄱ ㅈㅎㄷ ㅎ
를 이용한다. "인수보다 5가 작다"는 것은 "인수가 5보다 크다"와 똑같은 말이다. 그래서 '평범한 한글'에서는 따로 '보다 크다' 함수가 없다.
ㄴ ㄷ ㄹ ㅁ ㅂ ㅅ ㅈ ㄱㄴㄱ ㄴㄴㄱ ㄷㄴㄱ ㅁㄹ ㅎㄷㄴㄱ ㄴㄱ ㄱㅇㄱ ㅅㅎㄷ ㄴㄱ ㄴ ㅎㄷ ㅎ ㅅㅂ ㅎㄷ
[1, 3, 5, 7, 9]
- [1, 2, 3, 4, 5, 6 7, 8, 9, 10] 목록 중 홀수룰 선별한다. 어떤 값이 홀수인지 짝수인지 알아보는 방법은 많이 있지만 여기서는 을 이용한다. -1을 홀수번 곱하면 -1이, 짝수번 곱하면 1이 나오기 때문.
ㄴㄱ ㄱㅇㄱ ㅅㅎㄷ ㄴㄱ ㄴ ㅎㄷ ㅎ
는 인지 판별하는 함수이다.
'평범한 한글'에서 목록을 다루는 방법은 이 밖에 굉장히 다양하다. 하지만 목록 다루는 기본 기술을 잘 갈고 닦다 보면 '함수형 프로그래밍'을 좀 더 몸으로 체험할 수 있으리라 생각한다.