2014/12/17

Perl과 Ruby, Python의 함수 호출과 함수 참조에 대한 차이

루비와 파이썬에서 함수 호출과 함수 참조에 대한 차이
라는 글을 보고 Perl에서는 어떤지 한번 정리해 봅니다. 우선 윗글에 있는 코드를 Perl식으로 바꾸면 아래와 같습니다.

#!/usr/bin/env perl

sub a {
    print 'a';
}

sub b {
    print 'b';
}

sub f {
    my ($arg1, $arg2) = @_;
    $arg1;
}

f(a,b);


Perl에서는 Ruby,Python과 달리 변수에는 sigil이라고 하는 $를 앞에 붙여서 변수를 만듭니다. 따라서 변수명과 함수명이 같아도 엄연히 다른것이며 충돌하거나 암묵적으로 호출될 일은 없습니다.
위의 f함수에서처럼 인자로 그냥 bareword를(여기서는 a b) 사용했을경우 선언된 함수이름이 존재하면 함수를 호출하게 됩니다. 따라서 f(a,b)는 a함수, b함수 가 호출된 결과가 들어가게 됩니다. 각 함수가 차례로 호출되면서 ab가 차례로 찍히고 Perl은 { }블록형태의 함수에서 return문으로 명시적으로 리턴하지 않으면 블록내에서 마지막으로 평가된 값이 리턴됩니다. 여기서는 print 문이 성공하면 참(1)값을 돌려주기 때문에 각 함수의 마지막 평가값은 1이 되어서 결과적으로는 f(1,1)이 호출되고 f함수에서 마지막 평가값인 $arg1의 값은 1이 되어 f함수의 리턴값은 1이 됩니다. 마츠가 루비를 만들때 Perl을 많이 참고로 해서 그런지는 모르겠지만 여기 까지는 Ruby와 비슷하게 동작하는 것 같네요.

그럼 Python과 같이 함수의 참조가 넘어가서 f함수 내부에서 넘겨받은 함수 참조를 통해 호출하려면 Perl에서는 어떻게 해야 할까요?

#!/usr/bin/env perl

my $a = sub { print 'a'; };

sub b {
   print 'b';
}

my $b = \&main::b;   # 같은 패키지 내부면 my $b = \&b; 로 해도 됨

sub f {
    my ($arg1, $arg2) = @_;
    $arg1->();
    &$arg2;
}

f($a,$b);


Perl에서 함수레퍼런스는 함수가 호출되는 메모리상의 주소값을 가진 스칼라 변수 입니다. $같은 sigil이 없는 Python이나 Ruby에서는 동일한 이름을 가진 변수와 함수의 식별자(identifier)가 때로는 값 혹은 호출로 때로는 값 혹은 참조로 암묵적으로 동작하게 되지만 Perl에서는 확실히 구별됩니다. 위 Perl코드의 $a는 익명함수의 주소값을 $a라는 변수에 넣은것이고 $b는 이미 Perl의 기본 네임스페이스인 main패키지에 선언되어 심볼테이블에 기록된 b함수의 주소값을 $b변수에 넣은것입니다. 이제 두가지 방법으로 스칼라변수 $a, $b에 저장된 함수레퍼런스를 f함수에 넣어 호출하게 되면 f함수 내부에서 넘겨받은 함수 레퍼런스를 통해 함수를 호출 할때는 $arg1->() 처럼 뒤에 ->()를 붙여서 호출할 수도 있고 &$arg2 처럼 앞에 &를 붙여서도 호출 가능합니다.  Perl이 레퍼런스를 다루고 호출하는 문법을 보면 C나 Javascript 와 유사한 느낌적인 느낌이 들지 않나요?

Perl의 sigil이 코드를 noisy하게 보인다고 까는 사람도 있지만 저는 코드의 앞뒤를 뒤져보지 않아도 바로 이 코드의 의도를 명확하게 구별하고 알려주는 효자 같은 존재라고 봅니다.


PS: Perl 문법이 더 궁금한 분은 https://github.com/aero/perl_docs 을 참고하세요~