따라서 중간 위치에서 데이터의 삽입과 삭제가 많이 일어나는 에디터 등을 구현해야 할 때 적용할만 하다.
관련 문제로 백준 1406번을 풀었다. 연결리스트를 (간략화 버전으로) 직접 구현하여 풀어보기도 했고, CPP의 list 라이브러리(연결 리스트의 구현체)를 이용하여 풀어보기도 했다.
전반적으로 어렵지 않게 해결할 수 있었으나, list 라이브러리를 사용한 풀이에서 어려움을 겪었다. 계속해서 예기치 않은 세그먼트 오류가 발생했고, 디버거를 켜보니 데이터를 삭제(erase)하는 부분 이후 다시 데이터를 추가(insert)할 때 오류가 발생하였다. 고민해보니 삭제 부분에서 수정이 필요함을 알 수 있었다. 삭제 부분은 기존에 아래와 같이 구현했다.
// list<char> li;
// } else if (cmd == 'B') {
li.erase(cursor);
// }
멤버 함수인 erase를 호출하고 다른 처리는 하지 않았는데, 문제는 삭제 후에도 이터레이터(cursor)의 값이 변경되지 않았다는 점이다. if문을 나간 후 다음 for문으로 들어왔을 때, 이터레이터는 여전히 삭제된 항목의 위치를 가리키게 된다. 따라서 이후 해당 이터레이터로 리스트에 접근하면 오류가 발생하는 것이다.
// list<char> li;
// } else if (cmd == 'B') {
if(cursor != li.begin()) {
cursor--;
cursor = li.erase(cursor);
}
// }
이 문제는 위와 같이 수정하여 해결해야 한다. erase 멤버함수는 리스트에서 해당 이터레이터에 해당하는 데이터를 삭제한 후 그 바로 다음 데이터 위치에 해당하는 이터레이터를 반환한다. 이후 반환받은 이터레이터를 참조하면 safe한 처리가 가능하다.
로버트 나이스트롬(Robert Nystrom)의 Crafting Interpreters를 읽기 시작했다.
얼마 전 자바스크립트의 클로져 개념을 공부하는 도중, 답답한 마음에 V8 엔진의 소스코드를 뜯어보려고 시도했다. 자바스크립트 엔진은 클로져를 어떻게 처리하는지 궁금했기 때문이다.
결과는 실패였다. 코드는 너무나도 방대했고, 문서는 소스코드 자체를 설명해주는 글이 아니라 소스코드를 빌드하여 라이브러리로 활용하기 위한 방법을 안내하는 가이드같았다.
그러나 완전히 허탕을 친 것 만은 아니다. 이를 계기로 프로그래밍 언어로 씌여진 소스코드가 양방향의 텍스트라는 것을 자각하게 되었다. 코드는 1차적으로 1인 이상의 개발자가 쓰고 읽는다. 그러나 그 전에 코드는 컴퓨터에 대한 명령문이다.
즉, 코드는 두 부류(인간과 기계)의 독자를 염두에 두고 쓰여지는 텍스트라는 점에서 양방향 텍스트라고 할 수 있다. 우리는 컴퓨터가 처음 개발된 당시, 즉 이진 코드로 직접 장치를 제어했던 시대를 겪지 못했기 때문에 쉽게 자각하지 못하지만 말이다.
현재의 프로그래밍 언어는 인간과 기계가 모두 참조할 수 있는 공통의 텍스트를 만들기 위한 선배 개발자들의 노력의 결과물이라고 할 수 있다. 이들은 두 목적을 모두 실현하기 위해 정형화된 형식에 의해 조직되는 형식적 언어로 프로그래밍 언어를 구성하게 된 것이다.
다시 말해, 결과적으로 프로그래밍 언어는 양방향으로의 번역이 모두 용이한 중간 형태를 띄게 되었다. 개발자는 이진 코드를 직접 작성하는 것보다 적은 노력을 들여 소스코드를 작성하고 읽을 수 있으며, 컴파일러나 인터프리터를 통해 소스코드를 변환하기만 하면 기계도 그 명령을 수행할 수 있다.
자바스크립트와 정처기에 신물이 났던 모양인지 이 일련의 과정에 관심이 꽂혀서, 오토마타 이론과 컴파일러에 대한 자료를 마구 찾아보기 시작했다. 그러나 당장 내일 Vue.js 수업을 듣게 될 예정인 문과 비전공자 입장에서는 진입장벽이 너무 높았고, 대신 후기가 좋은 나이스트롬의 책을 읽어보려고 한다.
이 책은 Lox 언어 인터프리터를 단계적으로 직접 개발해보는 내용을 담고 있다. Lox 언어는 저자가 디자인한 것으로, 자바스크립트와 비슷하게 생긴 C Family 프로그래밍 언어이다. 나를 괴롭혔던 클로져를 구현하는 내용도 실려있다. 하나 하나 실습해보고 소소하게 정리해보자.