복수의 언어를 비교해서 학습할 때 알게 되는 것이 있다.
그것은 '규칙은 언어마다 다르다'는 것이다.
프로그래밍 언어의 교과서에는 다양한 규약이 기술되어 있지만, 그것은 절대적인 약속이 아니다.
'지금은 이렇게 약속하는 게 보다 수월하다고 생각하니 그렇게 합시다'라는 의미일 뿐이다.
특정 언어로 프로그래밍을 배우는 것이 아니라, 다수의 언어를 비교해 가면서 학습하는 것을 의미한다. 이를 통해, 무엇이 언어에 따라 다르고 무엇이 공통적인지 배울 수 있다.
언어가 어떻게 바뀌었고 바뀌기 전에는 어떤 의문점이 존재했는지 학습하는 것을 의미한다.
이를 통해, 언어가 가지고 있는 다양한 기능이 '왜' 탄생했는지 배울 수 있다.
프로그래밍 언어 Perl의 설계자인 Larry Wall은 프로그래머가 가져야 할 3가지 자질로서 '나태,조바심,자만심'을 제안했다.
어떤 언어가 자신의 목적에 적합한지는 그 언어를 사용할 때 자신이 어느 정도의 생산성을 발휘할 수 있는지로 판단할 수 있다.
다른 사람이 말하는 것에 혹하지 말고, 좋은 도구를 현명하게 선택하여 적재적소에 사용하도록 하자.
언어 설계자가 정한 규칙이 바로 문법이다. 문법은 언어에 따라 다르다. 연산자의 종류도 언어에 따라 다르다.
프로그래밍 언어의 결과물은 언어 처리계가 틀린 곳을 자세히 지적해준다. 그것을 이해하려 하지 않고서는 언어 처리계와 제대로 된 커뮤니케이션을 할 수가 없다.
'무엇을 배우면 좋습니까?' 이 질문에 대답하기 전에 우선 한 가지를 물어보고 싶다.
'여러분은 무엇을 만들고 싶은가요?' 목적이 명확하지 않으면 최선의 방법 또한 가르쳐줄 수 없다. 즉, 목적을 명확히 하는 것이다. 그리고 목적을 달성하기 위해 필요한 것부터 배우면 된다.
이 장에서는 if 문, while 문, for 문 등의 처리 흐름을 제어하기 위한 규칙(문법)에 대해서 설명하고 있다.
프로그래밍 언어를 배울 때나 혼자서 만들고 있는 작은 규모의 프로그램의 경우에는 Python과 같이 바로 예외를 던지는 쪽이 JavaScript 같이 undefined 처리하는 것보다 좋다.
무언가가 이상하다면 빨리 문제를 발견하는 것이 중요하다.
'이상하면 처리를 정지하고 빨리 보고해야 한다'는 설계 이념을 '패일 퍼스트'(fail first)라고 부른다. 소프트웨어의 목적에 따라서는 간단하게 정지되면 곤란한 경우도 있지만, 적어도 학습이나 개발 단계에서는 틀리면 바로 틀린 것을 발견하는 것이 오히려 이점이 많다.
스코프(Scope)란 이름의 유효 범위다. 프로그램 전체에서 이름이 충돌하지 않도록 관리하는 것은 어려운 일이다. 그래서 이름의 유효 범위를 좁게 한정해서 관리가 편해지도록 한다.
형은 사람이 데이터에 붙인 '추가 데이터'이다.
같은 비트열이라도 '그것을 어떤 종류의 값으로 해석할지'에 따라 틀린 값이 되어버린다. 이를 피하기 위해 '어떤 종류의 값인가?'라는 정보를 추가한 것이 형의 시작이다.
소스 코드를 읽을 때는 우선 디렉터리 구조와 파일명을 본다. 그리고 파일을 속독으로 읽고 거기서 정의하고 있는 함수나 클래스 이름, 자주 호출되는 함수명 등을 본다.
'우선 대략적인 구조를 잡고, 조금씩 상세한 정보로 접근한다'는 공통점이 있다.
소스 코드에는 다른 방식의 독해 방법이 있다. 디버거(debugger)의 과정을 사용해서, 실행되는 순서나 호출 계층으로 읽는 방법이다. 이 경우도 동일하게 우선은 대략적인 처리 흐름을 따라가고, 조금씩 깊이를 더해서 함수안의 처리를 따라가는 것이 중요하다.
요소 개수가 적을 때는 배열과 연결 리스트 중 어느 쪽을 사용해도 그다지 큰 차이가 없다. 그러나 요소 개수가 늘어나면 배열은 점점 처리 시간이 늘어나는 반면, 연결 리스트에서는 처리 시간에 변화가 없다. 요소 수가 많고 삽입이 빈번히 발생하는 경우에는 연결 리스트가 적합하다.
O(1) < O(log n) < O(n) < O(n²)
'결국 어떤 것을 사용하면 좋은 것일까?'라는 의문을 품을 수도 있다.
어떤 목적이든 사용할 수 있는 최선의 컨테이너는 없다.
컨테이너를 사용하는 목적, 어떤 사용법을 적용할 것인가, 어떤 조작이 많은가에 따라 최적의 컨테이너도 달라진다. 메모리를 절약할 필요가 있는가, 아니면 계산 시간을 줄일 필요가 있는가? 또는 어느 쪽도 절약할 필요가 없는지, 절대 정답은 존재하지 않는다.
자신의 상황에 맞게 적합한 균형을 찾는게 중요하다.
문자열에도 여러 가지 차이가 있다.
우선 '무엇이 문자인지'(문자 집합)의 차이, 다음으로 '어떻게 문자를 비트열로 표현하는지'(문자 부호화 방식)의 차이, 그리고 '어떤 정보를 어떤 메모리에 저장하는지'(문자열 구현)의 차이다. 많은 언어가 문자열을 지원하고 있지만, 서로 동일하다고 말할 순 없다.
현재 우리들이 사용하고 있는 PC에서는 음악을 들으면서 문서를 작성하고 브라우저로 웹 서핑을 하는 등 복수의 처리를 동시에 실행할 수 있다. 이와 같이 복수의 처리를 시간축 상에 오버랩에서 실행하는 것을 병행 처리라고 한다.
공유된 X와 Y를 변경하는 처리 A와 처리 B가 있다고 하자.
A가 'X를 락시키고, Y를 락시킨다'는 순서로 락을 걸어두고, B가 'Y를 락시키고, X를 락 시킨다'는 순서로 락을 걸었을 경우 시점에 따라선 문제가 발생한다.
A가 X를 락하고 B가 Y를 락한 상태에서 서로가 상대방 락이 풀리는 것을 기다리게 된다.
이것이 교착 상태(Deadlock, 데드락)라고 불리는 현상이다.
이 문제를 방지하기 위해서 프로그래머는 프로그램 전체에서 락의 순서가 일관되도록 주의해야 한다. '무엇에 락을 걸어야 하는가'뿐만 아니라, '어떤 순으로 락을 걸어야 하는가'도 파악해야 한다.
우리들은 어떻게 세계를 이해하고 있을까? 자신이 경험한 것을 '사물(object)'이라는 개념으로 정리해두고 있다. '사물(object)'이란 테이블과 의자나 은행 대출과 수식,사람,다항식,삼각형,트랜지스터 같은 것이다. 그리고 우리들의 사고, 언어, 행동은 이런 '사물'을 가리키거나 설명하거나 조작하기 위해 만들어졌다.
우리가 컴퓨터로 문제를 해결하고 있을 때, 현실 세계의 '사물(object)'모형(model)을 컴퓨터 안에 만들 필요가 있다.
현실 세계에 있는 '사물(object)'의 '모형(model)'을 컴퓨터 안에 만들려면 어떻게 하면 될까?
어떻게 하면 보다 편해질 수 있을까?
=> '객체 지향'이라는 개념이 탄생
모듈은 '하나로 모으는 기법'이다.
function makeCounter(){
var count = 0;
function push(){
count++;
console.log(count);
}
return push;
}
c = makeCounter();
c(); // -> 1
c(); // -> 2
c(); // -> 3
클로저(closure)라는 명칭에서 무언가를 닫는다는 느낌이 든다.
함수 push는 변수 count를 참조하지만 이것은 함수 push 안에 정의되어 있지 않다.
이런 변수를 자유 변수라고 한다.
함수 push는 자유 변수를 포함하고 있기 때문에 열린 함수다.
그리고 함수 makeCounter 대응표에서는 0이라는 값과 count라는 이름이 연결되어 있다.
값에 이름을 연결하는 것을 '바인딩'이라고 한다.
열린 함수 push가 makeCounter 대응표와 세트가 됨으로, 더 이상 그 이외의 스코프에 변수정의를 찾으러 가지 않아도 되는 완결된 상태가 된다.
이것을 '닫았다'라고 표현한다.
상속을 사용해서 코드를 재사용하면 코딩량이 줄어서 편하다.
하지만 상속을 반복하면, 코드 영향 범위가 넓어져서 이해하기 어렵게 된다. 이해하기 쉽게 하기 위해서는 상속 트리의 깊이를 낮추는 것이 중요하다.
방대한 정보 앞에 좌절될 때 어떻게 하면 좋을까?
저자는 시간을 정해서 '25분간 어디까지 베낄 수 있는지' 도전하는 것을 좋아한다.
분량으로 나누는 것도 좋은 방법이다.
중요한 것은 간격을 적절히 해서 목표를 이루었다는 만족감을 얻을 수 있도록 하는 것이다.
How(어떻게 만들까?)에 주력하기 쉽지만, What(무엇을 만들까?), Why(왜 만들까?)도 잊어서는 안 된다. How는 수단에 불과하다. 또한 시간은 유한한 자원이다. 의욕도 중요한 자원이다.