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
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