Ruby의 closure 예제

Ruby에서 closure를 어떻게 사용하는가.

(1) yield

class Array  
  def inject(n)  
     each { |value| n = yield(n, value) }  
     n  
  end  
  def sum  
    inject(0) { |n, value| n + value }  
  end  
  def product  
    inject(1) { |n, value| n * value }  
  end  
end  
[ 1, 2, 3, 4, 5 ].sum  ≫ 15  
[ 1, 2, 3, 4, 5 ].product  ≫ 120  

sum의 경우, 호출되는 함수는 inject(0)이며, inject에는 0과 블럭이 인자로 넘어간다. 따라서 sum의 반환값은 inject(0)의 반환값이 되고, 이 경우 그 반환값은 inject에서 “n”으로 정했다. inject에서는 인자로 받은 closure를 yield를 사용해 호출한다.

(2) Proc

def nTimes(aThing)  
  return proc { |n| aThing * n }  
end  
 
p1 = nTimes(23)  
p1.call(3)  » 69  
p1.call(4)  » 92  
p2 = nTimes("Hello ")  
p2.call(3)  » "Hello Hello Hello "  

Proc#call 을 사용해 closure를 반환하는 nTimes를 호출한다. call에 주어진 인자는 nTimes에서 n으로 받는다.

(3) &변수명

class Foo
    def initialize(&act)
        @action = act
    end

    def fire
        @action.call(3)
    end
end

f = Foo.new { |n| n * 2 }
puts f.fire

&act와 같이 &가 주어지면 메소드 뒤의 블럭을 Proc 클래스 타입의 인스턴스로 받아온다. 다음, action이라는 인스턴스 변수에 act를 저장한다. fire가 호출되면 Proc#call을 사용해 closure를 호출한다. 이 예에서, call의 인자는 |n|으로 받는다.

(4) Gotcha

irb(main):001:0> &a = { |n| n * 2 }
SyntaxError: compile error
(irb):1: syntax error
&a = { |n| n * 2 }
 ^
(irb):1: syntax error
&a = { |n| n * 2 }
        ^
(irb):1: syntax error
        from (irb):1

이 경우, &는 메소드 뒤에 오는 블럭을 받기 위한 용도이므로 실패한다. 그러나

irb(main):002:0> a = proc { |n| n * 2 }
=> #<proc :0x02c61c48@(irb):2>

이와같이 proc 형태의 인스턴스로는 받는 것은 가능하다.

(5) 다른 언어에서는 어떻게 해야할까?

당연히 자바라면 anonymous class를 쓸 것이고, C#에서도 그에 적절한 대응품을 쓰면 될 것입니다. 그런 것들은 생략하고 C++로 간단하게 2개 숫자를 더하는 예제를 짜봤습니다.

#include

using namespace std;

template
struct add
{
T operator()(T t1, T t2)
{
return t1 + t2;
}
};

template
U foo(U u1, U u2)
{
return T()(u1, u2);
}

int main()
{
cout < < foo, int>(1, 2) < < endl; return EXIT_SUCCESS; } [/code] 여기서 add<int>가 우리가 원하는 함수이고 foo< ..., int> 의 두번째 int 는 뒤에오는 인자의 타입들입니다. Ruby의 예와 달리 array를 쓰지 않은 것은 단지 예제가 너무 복잡해질까봐의 우려 때문입니다.

Similar Posts:

Post a Comment

Your email is never published nor shared.