여러분 프로그래밍 언어는 이런거 됩니까
(2004년 3월 2일 화요일)
어느 날, 여러분은 코드를 훑어보다가 언뜻 보기에 거의 똑같은 코드 블록들을 발견 합니다.
// 시시한 예제: alert("I'd like some Spaghetti!"); alert("I'd like some Chocolate Moose!");
function SwedishChef( food ) { alert("I'd like some " + food + "!"); } SwedishChef("Spaghetti"); SwedishChef("Chocolate Moose");
이 코드는 처음보다 더 낫습니다. 유지보수성, 가독성, 추상화 등등의 이유 때문에.
다른 예제를 살펴보겠습니다.
alert("get the lobster"); PutInPot("lobster"); PutInPot("water"); alert("get the chicken"); BoomBoom("chicken"); BoomBoom("coconut");
중복을 없애려면 함수를 다른 함수의 인자로 넘겨야 합니다. 함수를 인자로 넘기는 능력은 굉장히 중요합니다.
함수를 인자로 넘길 수 있다면 좀 더 깊은 수준에서 중복된 코드를 찾아 제거할 수 있습니다.
function Cook( i1, i2, f ) { alert("get the " + i1); f(i1); f(i2); } Cook( "lobster", "water", PutInPot ); Cook( "chicken", "coconut", BoomBoom );
보세요! 우리는 방금 함수를 인자로 넘겼습니다.
여러분의 언어는 이렇게 할 수 있나요?
잠시만... 하나 더.
함수 PutInPot, BoomBoom을 미리 정의해야 할까요?
이 함수들을 다른 곳에서 선언하지 않고, 인라인으로 정의해서 넘기면 더 멋지지 않을까요?
Cook( "lobster", "water", function(x) { alert("pot " + x); } ); Cook( "chicken", "coconut", function(x) { alert("boom " + x); } );
와우, 정말 깔끔하군요. 필요한 시점에 함수를 바로 정의해서 다른 함수에 인자로 넘겼습니다.
함수 이름을 짓는 귀찮은 짓을 하지 않아도 됩니다.
몇 걸음 더 나가 봅시다.
아래와 같은 코드가 여기저기 흩어져 있군요.
배열 각 요소에 뭔가를 하는 작업 말이에요
var a = [1,2,3]; for (i=0; i<a.length; i++) { a[i] = a[i] * 2; } for (i=0; i<a.length; i++) { alert(a[i]); }
배열 모든 요소에 뭔가를 하는 작업은 공통이니까, 다음과 같은 함수를 만듭니다.
function map(fn, a) { for (i = 0; i < a.length; i++) { a[i] = fn(a[i]); } }
이제 이 함수를 사용하면 위 코드는 이렇게 됩니다.
map( function(x){return x*2;}, a ); map( alert, a );
(마지막입니다 집중 하세요)
배열의 모든 값을 더하는 또 다른 예제입니다.
function sum(a) { var s = 0; for (i = 0; i < a.length; i++) s += a[i]; return s; } function join(a) { var s = ""; for (i = 0; i < a.length; i++) s += a[i]; return s; } alert(sum([1,2,3])); alert(join(["a","b","c"]));
sum과 join은 거의 똑같아요.
추상화 하고 싶으니까, 공통(generic) 함수를 만듭니다.
function reduce(fn, a, init) { var s = init; for (i = 0; i < a.length; i++) s = fn( s, a[i] ); return s; } function sum(a) { return reduce( function(a, b){ return a + b; }, a, 0 ); } function join(a) { return reduce( function(a, b){ return a + b; }, a, "" ); }
(끝!)
(이 까지는 내가 아는 부분 이었고, C/C++을 사용하는 나는 별로 와닿지 않았다.
그래서 뭐?)
...중략
모든 배열 요소 각각을 대상으로 연산을 수행하는 하찮은 부분을 추상화해낼 수 있는 능력이 뭐 그리 대단하다고 난리를 치냐고요?
위의 map함수를 다시 보면,
모든 배열 요소 각각을 대상으로 연산을 수행한다면, 작업 순서는 전혀 문제되지 않습니다.
그렇죠? CPU가 n개일 때, 각 CPU가 배열을 1/n씩 나누어 처리하도록 코드를 작성했더니
브라보, 갑자기 map 함수가 n배 빨라집니다.
만약, 인터넷에 올라온 모든 웹 페이지를 담을 만큼 엄청나게 큰 배열이 있다면?
모든 웹 페이지를 대상으로 검색을 한다면?
단순 문자열 검색 함수를 하나 만들어 수만 대 서버에서 돌아가는 map함수에 인자로 넘기기만 하면
수만 대 서버가 일을 나눠서 하기 때문에 번개같이 결과가 튀어 나옵니다.
와우!
..........................................................
이것이 구글의 맵리듀스 개념이라고 한다.
조엘이 2004년에 작성한 이 글을 이제야 읽고 이제야 이런 개념을 이해를 하다니...
나의 문제인가, 우리나라 IT 회사/문화의 문제인가,
<출처>
1. http://www.joelonsoftware.com/items/2006/08/01.html
2. 조엘온 소프트웨어를 넘어서, 221p