중요한 논문 읽기: “GOTO문은 해롭다”

현대 프로그래머들은 GOTO문이 나쁜 코딩이라는 것을 잘 알고있다. 초심자를 위한 프로그래밍 튜토리얼들은 GOTO문을 아예 생략하거나 금기시한다. 스파게티 코드를 향한 지름길이어서 그런데, 왜 그럴까?

1968년 3월에 저명한 컴퓨터 과학자 Edsger W. Dijkstra는 Communications of the ACM에 “GOTO문은 해롭다 (Go To Statement Considered Harmful)“이란 제목의 독자 투고를 했다.

다이크스트라는 우선 프로그램을 저장하는 텍스트 파일과, 프로그램을 실행하는 프로세스를 구분짓는다. 그는 프로그래머들이 후자를 이해하기가 더 어렵다고 말한다.

우리의 지적 능력은 정적인 관계를 이해하는데 맞추어 있고, 시간에 따라 변화하는 프로세스를 이해하는 능력은 상당히 떨어진다. 따라서 우리는......프로그램과 프로세스가 최대한 단순하게 상응하도록 해야 한다.

프로그램 파일에선 줄 번호가 프로그램문의 공간적인 좌표다. 반면에 코드를 실행하는 프로세스를 이해하려면 시간적인 좌표가 필요하다. 만약 프로그램이 간단한 프로그램문과 조건절로 이루어져 있다면 이 시간 좌표를 구하기 쉽다.

만약 프로그램에 대입문만 있다면 프로그램 텍스트에서 두 대입문 사이를 가리키면 충분하다......만약 조건문을 포함한다면......여전히 텍스트 상의 좌표를 가리키면 된다.

서브루틴에서 서브루틴을 호출한다면 여러 좌표의 순서쌍이 필요하다.

서브루틴을 포함한다면 프로세스의 진척 상황을 여러 좌표의 순서쌍으로 나타낼 수 있으며, 이 순서쌍의 길이는 현재 호출된 서브루틴의 깊이이다.

루프가 있다면 숫자 하나로 현재 절차가 몇 번 반복됬는지를 추적할 수 있다. for (int i = 0; i < max; i++) 에서 i가 이 역할을 한다.

이런 시간적 좌표는 알고리즘적으로 자동 생성될 수 있다. 따라서 시간 좌표를 계산해서, 우리는 프로그램이 현재 “어디에” 있는지를 알 수 있다.

GOTO문을 자주 사용한다면 시간적 좌표를 생성할 수 있는 간단한 기계화 가능한 알고리즘이 없다. 오직 프로세스 전체를 직접 실행해보는 수 밖에는 없기에, 의미 없는 좌표값이 주어진다.

GOTO문의 통제 없는 사용의 직접적인 결과는 프로세스의 진척 상황을 나타내는 좌표를 찾기가 굉장히 어렵다는 것이다.

프로그램문 10개를 실행한 후, func() 함수를 호출하는 main() 함수가 있고, func()는 루프를 실행하는데, 현재 루프를 다섯 번 반복했다. 그러면 프로그램이 현재 “어디에” 있는지 나타내기가 쉽다: main 10, func 1, iteration 5. 사람이 이해하기 쉬운 좌표다. 반면 똑같은 코드가 GOTO문으로 쓰여졌다면 시간적 좌표는 의미없는 숫자일 것이다.

GOTO문은 현재 다이크스트라 본인이 개척한 구조화 프로그래밍 방법론들로 거의 대체되었다. 얼마 전 1992년도에 쓰여진 한 교과서를 읽다가 GOTO문을 발견했는데, GOTO문을 사용하기 적절한 아주 희귀한 예시여서 소개한다.

다음 코드는 0들 사이에 끼여있는 1을 0으로 바꾸고, 1들 사이에 끼어있는 0을 1로 바꾸는 유한 오토마타를 구현한다. “0000100001111011”이라는 문자열을 “0000000001111111”로 변환하는 식이다.

void bounce() {
  char x;
  /* state a */
  a: putchar('0');
  x = getchar();
  if (x == '0') goto a;
  if (x == '1') goto b;
  goto finis;

  /* state b */
  b: putchar('0');
  x = getchar();
  if (x == '0') goto a;
  if (x == '1') goto c;
  goto finis;

  /* state c */
  c: putchar('1');
  x = getchar();
  if (x == '0') goto d;
  if (x == '1') goto c;
  goto finis;

  /* state d */
  putchar('1');
  x = getchar();
  if (x == '0') goto a;
  if (x == '1') goto c;
  goto finis;

  finis: ;
}

실전에서는 유한 오토마타의 한 상태에서 다른 상태로 이동하는 것을 GOTO문으로 코딩하면 안된다. 전이표(transition table)는 배열로 나타내고 현재 상태는 변수로 나타내는 것이 적절하다.

허나 이 코드는 실전에서 사용하기 위함이 아니라, 이론적인 개념을 설명하기 위함이다. GOTO문을 사용하면 코드가 한 줄에서 다른 줄로 “점프”하는 것 처럼, 유한 오토마타도 입력 문자에 따라서 현 상태에서 다른 상태로 “점프”한다. 따라서 GOTO문은 유한 오토마타의 동작을 쉽게 나타내는 비유라고 볼 수 있다. 과거의 유물 GOTO문을 활용하는 희귀하지만, 기발한 코드인 것이다.

#코딩 #컴싸 #c언어