Search

인덱스가 0부터 시작하는 이유

이번 포스트의 제목은 Dijkstra 교수의 노트 원문 제목을 차용했습니다.

Intro

대부분의 프로그래밍 언어에서 array의 index는 0부터 시작합니다.
그리고 (slicing 등을 할 때) 구간의 마지막 index에 해당하는 값은 결과 집합에서 제외합니다.
예를 들어 my_list = [1, 2, 3] 일 때 my_list[1:2]처럼 slicing을 하면 결과는 [2]가 나옵니다.
프로그래밍을 처음 배울 때 굉장히 헷갈리는 부분 중에 하나입니다.
컴퓨터 과학에서는 왜 index를 1부터 시작하지 않고 0부터 시작하도록 만들었을까요?
또 마지막 index는 왜 결과에서 제외하는 것일까요?
최단 경로 알고리즘(a.k.a. Dijkstra 알고리즘)으로 유명한 독일의 컴퓨터 공학자 Dijkstra 교수는 numbering을 0부터 시작해야 하는 이유를 제시했고 대부분의 언어에서 그의 논증에 따라 zero-based numbering을 채택 했습니다.
이번 글에서는 Dijkstra 교수가 제시한 numbering을 0에서 시작해야하는 이유에 대해 알아보고 이를 코드에 적용 했을 때 어떤 장점이 있는지 얘기 해보겠습니다.

Comparison Expressions

Zero-based numbering을 얘기하기 전에 효율적인 비교 표현식 대해 알아봅시다.
2, 3, ..., 12를 ... 없이 표현하려면 다음과 같은 네 가지의 선택지가 있습니다.
a) 2 ≤ i < 13
b) 1 < i ≤ 12
c) 2 ≤ i ≤ 12
d) 1 < i < 13
여러분은 어떤 표기법을 선호하시나요? 위의 네 가지의 표기법 사이에 우위가 있을까요?
Dijkstra 교수가 제시한 의견을 알아봅시다.
먼저 bd처럼 앞에 부등호가 있는 경우, 시작하는 숫자를 명확히 나타내지 않습니다.
2 ≤ i처럼 표기 했을 때 직관적으로 시작하는 숫자가 2임을 알 수 있는 반면에
1 < i처럼 표기 했을 때는 시작하는 숫자를 명확히 알기 위해 i가 정수인지 실수인지 범위를 확인해야 합니다.
그럼 ac는 어떤 차이가 있을까요?
ab를 보면 비교식 양 끝 숫자의 차가 해당 sequence의 길이와 같습니다.
2부터 12까지의 숫자는 총 11개인데
a13 - 2 = 11, b12 - 1 = 11로 비교식 양 끝 숫자를 보면 빠르게 길이를 계산할 수 있습니다.
직관적으로 sequence의 길이를 알아낼 수 있다는 점에서 a, bc, d보다 우수합니다.
결정적으로 c는 공집합을 표현할 수 없습니다.
굳이 한다면 2 ≤ i ≤ 1 같은 형태로 나타내야 하지만 이는 기본조건(시작 수 < 끝 수)을 위배합니다.
위와 같은 이유를 종합했을 때 가장 합리적인 표기법은 a입니다.
sequence의 연속을 표현하기 위해서도 a가 가장 합리적입니다.
2 ≤ i < 13, 13 ≤ j < 24, ...

Base Index

위에서 정한 표기법을 기반으로 하여 N의 길이를 가진 sequence를 다룰 때 시작을 0으로 하는 것과 1로 하는 것의 차이에 대해 비교해보겠습니다.
먼저 시작이 1일 때 길이가 N인 sequence의 표기는 다음과 같습니다: 1 ≤ i < N+1
시작을 0으로 하면 다음과 같이 표기됩니다: 0 ≤ i < N
시작을 0으로 했을 때는 끝나는 수(N)를 보면 sequence의 길이를 알 수 있습니다.
또 연속적인 sequence에서 시작 수를 보면 이전까지의 elements의 개수를 알 수 있습니다.
0 ≤ i < 5, 5 ≤ j < 10, 10 ≤ k < 15처럼 있을 때 j 뒤의 10을 보면 전체 sequence의 길이가 10이라는 것을 알 수 있고, k 앞의 10을 보면 k 이전까지 숫자가 총 10개 있다(0~9)는 것을 알 수 있습니다.
index가 1에서 시작했을 때인 1 ≤ i < 6, 6 ≤ i < 11, 11 ≤ i < 16와 비교해보면 훨씬 직관적입니다.

In Programming

이제 zero-based numbering과 0 ≤ i < N 표기를 코드에 적용 했을 때 장점을 알아봅시다.
위에서 설명한 내용이 코드로 구현되었을 뿐입니다.
Python을 기준으로 설명하겠습니다.
1) 끝나는 숫자를 보면 해당 range 또는 list의 길이(elements의 개수)를 바로 알 수 있습니다:
range(3) list_[:3]
Python
복사
2) 끝점과 시작점을 빼면 slice 된 list의 길이를 바로 알 수 있습니다:
>>> a = list_[1:6] >>> len(a) 5
Python
복사
3) list를 두 개로 나눌 때 하나의 기준으로 쉽게 표현할 수 있습니다:
list_[:x] list_[x:]
Python
복사

Outro

if문에서 수의 범위를 나눌 때 비교 표현식을 어떻게 쓰는 것이 pythonic한 지 고민했던 적이 있습니다.
그 동안 궁금했던 indexing의 이유를 알게 되었고 보다 좋은 코드를 쓸 수 있게 되어 기쁘게 생각합니다.
아래의 코드가 예전과 다르게 보이면 좋겠습니다:
if 0 <= age < 7: print("Infant") elif 7 <= age < 20: print("Adolescence") else: print("Adult")
Python
복사

Reference