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