자바 7은 클로저를 지원할 듯

Posted by 大山 Sun, 20 Aug 2006 06:14:00 GMT

자바 7에서는 드디어 클로저를 지원하려나 보다. 지금이 자바 5이니, 자바 7이 언제 나오게 될지는 모를 일이지만.

모르는 분들을 위해 살짝 귀뜸하면, 클로저(Closure)는 코드 중에 함수를 직접 정의해서 객체로 변환시킨 걸 이르는 용어이다. 우리말로 하면, 익명 함수 정도가 될까? 흥미로운 건 이걸 다른 메소드에 인자로 넘길 수 있다는 거다. 예를 들면, 다음과 같은 식이다.

>> [1, 2, 3, 4, 5].map(&lambda {|i| i * 3})
=> [3, 6, 9, 12, 15]
 

위에서 lambda { ... } 부분이 클로저인데, 이는 i에 해당하는 수를 넘겨주면 그의 3배수를 리턴하는 익명 함수 객체이다. 위의 코드에서는 이 클로저를 배열의 map 메소드에 인자로 넘겨주고 있다. map 메소드는 배열의 각 원소에 넘겨받은 클로져를 적용해서, 새로운 배열을 만들어 리턴해준다. 이를 보다 루비스럽게 작성하면 다음처럼 되겠다.

>> [1, 2, 3, 4, 5].map {|i| i * 3}
=> [3, 6, 9, 12, 15]
 

루비에서는 클로저란 어려운 이름 대신, 이를 블록이라고 부른다. 다른 예를 하나 들어보자.

>> ["banana", "kiwi", "apple", "water melon"].sort
=> ["apple", "banana", "kiwi", "water melon"]
 

위의 코드에서 sort 메소드는 배열의 원소를 알파벳 순서로 정렬해준다. 그런데 sort에 블록을 붙여주면 정렬 법칙을 바꿀 수 있다. 다음 코드를 한번 보자.

>> ["banana", "kiwi", "water melon", "apple"].sort {|i, j| i.length <=> j.length}
=> ["kiwi", "apple", "banana", "water melon"]
 

위에서는 문자열의 길이를 비교하는 블록을 sort 메소드에 넘기고 있다. 결과적으로 리턴되는 배열도 문자열의 길이대로 정렬되어진다.

자바 5에서는 generic이 추가되더니 7에서는 클로져를 지원하는 등 자바도 갈수록 루비스러워지는 듯. 자바 7을 기다리는 그대 지금 루비로 오라. ;)

Posted in  | Tags , , , ,  | 22 comments | 2 trackbacks

루비가 쿨한 이유 2 - 블록

Posted by 大山 Tue, 25 Jul 2006 02:20:00 GMT

[참고: 이 글은 루비가 쿨한 이유 1 - irb에 이어지는 시리즈임.]

블록을 처음 접한 사람은 조금 당황하게 마련이다. 자바나 C 등의 기존 언어에서는 전혀 볼 수 없었던 기능이기 때문이다. 조금 새롭겠지만 정말로 강력하고 편리한 기능이므로 심호흡을 가다듬고 꼭 읽어보시길.

프로그래밍을 하다보면 배열의 내용물을 차례차례 하나씩 처리해야 하는 경우가 늘상 있다. 이런 경우에 자바와 같은 언어에서는 다음처럼 for 루프를 쓰는게 보통이다.

import java.util.*;

class PrintArray {
  public static void main(String[] argv) {
    String[] arr = {"mike", "nancy", "john", "cathy"};

    for (int i=0;  i<arr.length; i++) {
      System.out.println(arr[i].toUpperCase());
    }
  }
}
 

인덱스 변수를 따로 선언해서 써야 하는게 엘러건트하지 않기 때문에, 이터레이터(Iterator)를 쓰기도 한다.

import java.util.*;

class PrintArray {
  public static void main(String[] argv) {
    Iterator it = Arrays.asList({"mike", "nancy", "john", "cathy"}).iterator();

    while (it.hasNext()) {
      System.out.println(it.next().toUpperCase());
    }
  }
}
 

루비에서는 이렇게 한다.

["mike", "nancy", "john", "cathy"].each {|name| puts name.upcase}
 

여기서 { |name| puts name.upcase } 부분이 바로 블록이다. 배열의 each라는 메소드에 이 코드를 넘겨주면, each가 배열의 내용물을 차례차례 하나씩 거쳐가면서, 블록을 호출하게 된다. 즉 루비에서는 변수만 인자로 넘겨주는 것이 아니라, 실행할 수 있는 프로그램 코드를 인자로 넘겨주는 것도 가능한 것이다.

블록은 C의 함수 포인터와도 비슷한데 정확히는 클로저라는 개념이다. 이전까지는 Lisp이나 Smalltalk에서 주로 쓰이던 기능이라는 정도만 알아 두자. 그럼 블록을 사용하는 예를 몇 개 더 보도록 하자.

배열의 내용물을 하나씩 처리(문자열을 대문자로 변환)해서, 새로운 배열을 만드는 경우:

c:\> irb --simple-prompt
>> ["mike", "nancy", "john", "cathy"].map {|name| name.upcase}
=> ["MIKE", "NANCY", "JOHN", "CATHY"]
 

배열의 내용물 중에서 특정 조건(문자열에 "y"가 포함된 경우)을 만족시키는 내용물만 추려내는 경우:

>> ["mike", "nancy", "john", "cathy"].find_all {|name| name.include?("y")}
=> ["nancy", "cathy"]
 

배열의 내용물 전체가 특정 조건(짝수)을 만족시키는지 테스트하는 경우:

>> [2, 10, 8, 26, 32].all? {|num| num%2 == 0}
=> true
 

정말 엘러건트하지 않은가. 여기서 다룬 것은 블록의 가장 초보적인 예일 뿐이다. map, find_all, all? 같은 메소드는 모두 루비의 일반 메소드이다. 루비 프로그래머는 얼마든지 블록을 인자로 받는 새로운 메소드를 정의해서 사용할 수 있다.

이제 망설이지 말고 어서 루비를 설치해서 사용해 보자.

관련글: 루비가 쿨한 이유 3 - 해시

Posted in  | Tags ,  | no comments | no trackbacks