(Python) 리스트의 인덱스와 범위를 쉽게 이해하는 방법

리스트의 인덱스는 0부터 시작한다. 사실 많은 프로그래밍 언어에서 배열의 인덱스는 0부터 시작하기 때문에 “맨 첫원소가 0번이고 그 다음은 1번… N번째 원소는 N-1로 참조할 수 있다.”고 외워두면 그리 헷갈리지는 않는다.

그런데 이게 슬라이스 범위 문법에서는 또 헷갈린다. 그 이유는 슬라이스에서 뒤쪽 범위는 포함되지 않기 때문이다. 게다가 파이썬 리스트는 음수 인덱스를 사용해서 뒤에서부터 위치를 지정하는 것도 있다. 자, 첫번째 원소는 0번인데 뒤에서 부터 세면 -1 번부터 시작한다. 그렇다면 -5:-2는 어디서부터 어디까지일까?

이 혼란을 해결할 수 있는 아이디어가 하나 있어서 소개한다. 실제로 이것을 의도했는지는 확인할 수 없지만, 이 방식으로 리스트의 인덱스를 이해하면 방향에 무관하게 헷갈리지 않고 위치나 범위를 지정할 수 있다. 그것은 바로…

인덱스는 데이터가 아닌, 데이터와 데이터 사이의 공간을 가리킨다는 것이다. 알파벳 문자 7개로 구성된 리스트 ['A', 'B', 'C', 'D', 'E', 'F', 'G']를 예로 들어보자. 이해를 위해서 아래 그림을 보면 되겠다.

위 그림은 리스트에서 각 인덱스가 가리키는 위치를 나타내는 것이다. 0번은 ‘A’를 가리키는 것이 아니라 ‘A’의 바로 앞 경계부분에 위치한다. 따라서 aList[1]은 “1번 인덱스를 시작으로 1개의 원소”를 참조하게 된다. 이 범위는 그림으로 표현하면 아래와 같다.

이해가 되시는지? 정수 인덱스로 단일 원소를 참조하면, 항상 해당 위치의 인덱스로부터 오른쪽으로 1개만큼을 찾는 것이다. 이것은 음수 인덱스를 사용할 때에도 마찬가지로 적용된다. 리스트의 왼쪽 경계가 0이었던 것과 마찬가지로 오른쪽 경계도 0으로 잡는다. 따라서 aList[-1]은 -1 이 위치하는 F G의 사이에 해당하므로 “G”를 얻게 된다.

다음 aList[1:5]로 범위를 선택했을 때이다. 인덱스 1과 5 사이의 원소를 선택하면 된다. 인덱스는 각 원소와 원소 사이에 위치하므로 종료 지점이 포함되는지를 따질 이유가 없다.

음수 인덱스를 사용해서 범위를 선택할 때에도 마찬가지이다. aList[-5:-2]는 인덱스 -5와 -2 사이이기 때문에 ['C', 'D', 'E']가 된다. 역순으로 선택할 때에도 마찬가지로 이해하면 된다. aList[-2:-5:-1]은 -2~-5 범위를 역순으로 찾아가므로 ['E', 'D', 'C']를 선택하게 된다.

간단하면서도 모든 케이스에 딱딱 들어맞는 원리이다. 다시 정리하자면 다음과 같다.

  1. 리스트의 인덱스는 왼쪽 바깥, 오른쪽 바깥을 0으로 잡으며
  2. 원소와 원소사이를 가리키면서 1씩 증가하거나 감소한다.
  3. 범위로 슬라이싱할 때에는 두 인덱스 사이를 선택한다.
  4. 단일 원소를 찾을 때에는 해당 인덱스에서 무조건 오른쪽 1칸을 선택한다.

리스트에서 원소의 위치를 찾거나 범위를 선택하는데 어려움을 겪는 사람들에게 모쪼록도움이 되었으면 좋겠다.

Julia나 R 과 같은 과학/공학 친화적인 언어에서는 배열의 인덱스가 1부터 시작한다. 이런 언어들은 보통 범위를 정의할 때 범위의 stop 부분을 포함하는 경우가 많다. 이러한 언어의 디자인은 각 인덱스가 값 위치에 존재한다고 보면 이해가 쉽다. 배열의 시작을 1부터하면서 범위의 끝은 포함하지 않는 방식으로 디자인된 언어가 있는지는 모르겠는데, 아마도 매우 비직관적이어서 쓰는 사람을 골탕 먹일 의도가 아니라면 그렇게 만들지는 않을 것 같다.