[첫화면으로]Perl/서브루틴

마지막으로 [b]

Perl의 subroutine

1. perlsub document 요약
1.1. 개요
1.2. 설명
1.2.1. my()를 통한 프라이빗 변수 ( Private Variables via my() )
1.2.2. 영구 private 변수 ( Persistent Private Variables )
1.2.2.1. state()를 통한 영구 변수 ( Persistent variables via state() )
1.2.2.2. closure를 통한 영구 변수 ( Persistent variables with closures )
1.2.3. local()을 통한 임시값 ( Temporary Values via local() )
1.2.3.1. local()의 문법에 관하여 ( Grammatical note on local() )
1.2.3.2. 특수 변수들의 지역화 ( Localization of special variables )
1.2.3.3. glob 지역화 ( Localization of globs )
1.2.3.4. 구조형 변수의 원소 지역화 ( Localization of elements of composite types )
1.2.3.5. 구조형 변수의 원소를 지역화 후 삭제 ( Localized deletion of elements of composite types )
1.2.4. L밸류 서브루틴 ( Lvalue subroutines )
1.2.5. 심볼테이블 엔트리 넘겨주기 (typeglobs) ( Passing Symbol Table Entries (typeglobs) )
1.2.6. 여전히 local()을 사용해야 하는 경우들 ( When to Still Use local() )
1.2.7. 참조에 의한 전달 ( Pass by Reference )
1.2.8. 프로토타입 ( Prototypes )
1.2.9. 상수 함수 ( Constant Functions )
1.2.10. 내장 함수 오버라이드 ( Overriding Built-in Functions )
1.2.11. 자동 로드 ( Autoloading )
1.2.12. 서브루틴 속성 ( Subroutine Attributes )
1.3. 참고
2. 다른 문서들에서
2.1. 선언 순서
2.2. Context
2.3. Prototype
2.4. 인자로 레퍼런스 넘겨주기
2.5. 인자로 파일핸들 넘겨주기
2.6. Named parameter
2.7. 서브루틴의 레퍼런스
2.8. Closure
2.9. 서브루틴을 반환하는 서브루틴
2.10. closure variable를 static local variable로 쓰기
2.11. 서브루틴을 인자로 넘기기
3. 기타
4. Comments

1. perlsub document 요약

1.1. 개요

서브루틴 선언하기

    sub NAME;                     #  정의하기 전에 미리 선언하기 ("forward" declaration)
    sub NAME(PROTO);              #  마찬가지인데, 프로토타입 제시
    sub NAME : ATTRS;             #  속성 제시
    sub NAME(PROTO) : ATTRS;      #  프로토타입, 속성 제시

    sub NAME BLOCK                #  선언과 정의를 함께
    sub NAME(PROTO) BLOCK         #  마찬가지인데, 프로토타입 제시
    sub NAME : ATTRS BLOCK        #  속성 제시
    sub NAME(PROTO) : ATTRS BLOCK #  프로토타입, 속성 제시

런타임에 익명 서브루틴 정의하기:

    $subref = sub BLOCK;                 # 
    $subref = sub (PROTO) BLOCK;         # 프로토타입 제시
    $subref = sub : ATTRS BLOCK;         # 속성 제시
    $subref = sub (PROTO) : ATTRS BLOCK; # 프로토타입과 속성 제시

서브루틴 임포트하기:

    use MODULE qw(NAME1 NAME2 NAME3);

서브루틴 호출하기:

    NAME(LIST);    # 괄호를 사용하면 앞에 &는 생략 가능
    NAME LIST;     # 미리 선언되어 있거나 임포트한 경우는 괄호 생략가능
    &NAME(LIST);   # 프로토타입 무시하기
    &NAME;         # 현재 @_ 변수를 호출된 서브루틴에서 사용할 수 있게 함

1.2. 설명

사용자가 정의하는 서브루틴:

Perl에서의 함수 호출과 리턴값 모델:

넘겨진 인자들은 @_ 배열을 통해 접근할 수 있다.

return 구문:

Perl에는 이름 있는 형식 인자2를 지원하지 않는다.

예:

    sub max {
        my $max = shift(@_);
        foreach $foo (@_) {
            $max = $foo if $max < $foo;
        }
        return $max;
    }
    $bestday = max($mon,$tue,$wed,$thu,$fri);

    # 공백으로 시작하는 줄은 앞 줄에 이어지는 줄로 취급하여 한 줄을 구성

    sub get_line {
        $thisline = $lookahead;  # 전역 변수!
        LINE: while (defined($lookahead = <STDIN>)) {
            if ($lookahead =~ /^[ \t]/) {
                $thisline .= $lookahead;
            }
            else {
                last LINE;
            }
        }
        return $thisline;
    }

    $lookahead = <STDIN>;       # 첫번째 라인을 읽음
    while (defined($line = get_line())) {
        ...
    }

인자에 이름을 붙이기 위해서 프라이빗 변수 리스트에 할당:

    sub maybeset {
        my($key, $value) = @_;
        $Foo{$key} = $value unless $Foo{$key};
    }

할당은 값을 복사하여 이뤄지기 때문에, 위의 코드는 참조에 의한 호출을 값에 의한 호출로 전환하는 효과도 있다. 이렇게 전환하지 않는 경우 함수는 @_의 값을 그 자리에서 변경함으로써 호출자 쪽의 값을 바꿀 수 있다.

    upcase_in($v1, $v2);  # $v1과 $v2의 값이 바뀐다
    sub upcase_in {
        for (@_) { tr/a-z/A-Z/ }
    }

당연히 상수나 리터럴은 바꿀 수 없다. 시도할 경우 에러가 난다:

    upcase_in("frederick");

인자를 함수 내에서 변경하는 것보다는, 사본을 가지고 작업한 후 리턴하는 형태로 작성하는 게 훨씬 더 안전하다:

    ($v3, $v4) = upcase($v1, $v2);  # 여기서는 $v1과 $v2의 값이 바뀌지 않는다
    sub upcase {
        return unless defined wantarray;  # void context, do nothing
        my @parms = @_;
        for (@parms) { tr/a-z/A-Z/ }
        return wantarray ? @parms : $parms[0];
    }

Perl에서는 모든 인자가 @_에 담긴 하나의 평탄한 파라메터 리스트로 보이기 때문에, upcase()의 정의를 바꿀 필요 없이 다음과 같이 호출할 수도 있다:

    @newlist   = upcase(@list1, @list2);
    @newlist   = upcase( split /:/, $var );

그러나, 다음과 같이 하면 안 된다:

    (@a, @b)   = upcase(@list1, @list2);

리턴되는 리스트도 평탄화되기 때문에, 반환하는 모든 것이 @a에 담기고 @b는 빈 채로 남는다. 이에 대한 대안은 "Pass by Reference"를 참조

서브루틴을 호출할 때 명시적으로 & 접두어를 붙일 수 있다.

서브루틴이 재귀적으로 호출되는 경우:

    &foo(1,2,3);        # 인자 세 개를 전달
    foo(1,2,3);         # 위와 동

    foo();              # 빈 리스트를 전달
    &foo();             # 위와 동

    &foo;               # foo()는 현재 인자목록을 그대로 취한다, foo(@_)와 동일하다 !!
    foo;                # 서브루틴이 미리 선언된 경우에는(또한 그 경우에만) foo()와 동일하다
                        # 미리 선언되지 않았다면 bareword "foo"이다.

&를 붙인 형태는 인자 리스트를 생략가능하게 만들어주는 것 이외에도, 프로토타입 체크를 하지 않도록 하는 기능도 있다

이름이 대문자로만 구성된 서브루틴

BEGIN, UNITCHECK, CHECK, INIT, END

1.2.1. my()를 통한 프라이빗 변수 ( Private Variables via my() )

    my $foo;            # $foo를 렉시컬 지역 변수로 선언
    my (@wid, %get);    # 변수들의 리스트를 지역 변수로 선언
    my $foo = "flurp";  # $foo를 렉시컬로 선언하고 초기화
    my @oof = @bar;     # @oof를 렉시컬로 선언하고 초기화
    my $x : Foo = $y;   # 속성이 명시됨

경고: my 선언에서 속성 리스트를 사용하는 것은 개발 중인 기능이며, 시맨틱이나 인터페이스가 차후에 변경될 수 있다. Perldoc:attributes , Perldoc:Attribute::Handlers 참조.

my 연산자

local 연산자를 사용하여 생성된 동적 변수와 달리, my를 사용하여 선언한 렉시컬 변수는 호출된 서브루틴을 비롯한 외부로부터 완전히 감추어진다.

어떤 지점을 정적으로 둘러싸고 있는 스코프 내에서 선언된 my 변수는 볼 수 있다. 오직 동적 스코프만 차단된다.

    my $x = 10;
    sub bumpx { $x++ }

그러나 eval()구문은 이 구문이 평가되는 스코프 내에 있는 렉시컬 변수를, eval() 자신 내부에서 다시 선언하면서 변수의 이름을 감춰버리지 않는 이상은, 접근할 수 있다. Perldoc:perlref 참조.

원한다면 my()의 파라메터 리스트에 값을 할당함으로써 변수를 초기화할 수 있다.

    $arg = "fred";        # "전역" 변수
    $n = cube_root(27);
    print "$arg thinks the root is $n\n";
    sub cube_root {
        my $arg = shift;  # 변수이름은 아무래도 상관없다
        $arg **= 1/3;
        return $arg;
    }

fred thinks the root is 3

my는 단지 변경자일 뿐이라서, my의 인자 리스트에 있는 변수에 값을 할당할 때 변수가 스칼라인지 배열인지 여부에 영향을 주지 않는다

    my ($foo) = <STDIN>;                # WRONG?
    my @FOO = <STDIN>;
    my $foo = <STDIN>;
    my $foo, $bar = 1;                  # 잘못
    my $foo;
    $bar = 1;

선언된 변수는 현재 구문이 끝난 후에야 사용할 수 있다.

    my $x = $x;
    my $x = 123 and $x == 123

제어문의 경우는 실행되는 블록 뿐 아니라 조건식이 있는 부분도 같은 스코프의 일부이다.

    while (my $line = <>) {
        $line = lc $line;
    } continue {
        print $line;
    }
    if ((my $answer = <STDIN>) =~ /^yes$/i) {
        user_agrees();
    } elsif ($answer =~ /^no$/i) {
        user_disagrees();
    } else {
        chomp $answer;
        die "'$answer' is neither 'yes' nor 'no'";
    }

foreach 루프의 경우

    for my $i (1, 2, 3) {
        some_function();
    }

렉시컬 변수를 사용하는 걸 장려

    use strict 'vars';

my의 컴파일타임과 런타임 효과

my로 선언한 변수는 어느 패키지에도 속하지 않으므로, "fully qualified name"7을 쓸 수 없다.

    my $pack::var;      # 에러! 허용되지 않는 문법
    package main;
    local $x = 10;
    my    $x = 20;
    print "$x and $::x\n";  # "20 and 10" 출력

my 변수를 파일의 가장 바깥쪽 스코프에서 선언하여, 파일 외부의 동일한 식별자를 가진 변수들을 숨길 수 있다.

    my $secret_version = '1.001-beta';
    my $secret_sub = sub { print $secret_version };
    &$secret_sub();

1.2.2. 영구 private 변수 ( Persistent Private Variables )

영구적으로 값이 보존되는(persistent) 변수를 만드는 데는

1.2.2.1. state()를 통한 영구 변수 ( Persistent variables via state() )

my가 들어갈 자리에 대신 state 키워드 사용

    use feature 'state';
    sub gimme_another { state $x; return ++$x }

1.2.2.2. closure를 통한 영구 변수 ( Persistent variables with closures )

렉시컬 변수는 C 언어에서 auto 변수와 비슷하나,

C에서 함수 내 정적(static) 변수와 같은 효과를 내도록 하는 법

    {
        my $secret_val = 0;
        sub gimme_another {
            return ++$secret_val;
        }
    }
    # $secret_val은 블록 바깥쪽에서는 접근할 수 없다
    # 그러나 gimme_another의 매 호출 사이에도 값이 계속 유지된다
    BEGIN {
        my $secret_val = 0;
        sub gimme_another {
            return ++$secret_val;
        }
    }

가장 바깥쪽 스코프(파일 스코프)에서 선언된 렉시컬 변수는 C 언어에서 파일 스태틱10와 유사하게 동작한다.

1.2.3. local()을 통한 임시값 ( Temporary Values via local() )

경고: 일반적으로는 local 대신 my를 사용하라, 그것이 더 빠르고 안전하다.

    # 값의 지역화

    local $foo;                 # $foo를 동적으로 지역화
    local (@wid, %get);         # 변수의 리스트를 지역화
    local $foo = "flurp";       # $foo를 지역화하고 초기화
    local @oof = @bar;          # @oof를 지역화하고 초기화

    local $hash{key} = "val";   # 특정 해쉬 엔트리에 지역화된 값을 설정
    delete local $hash{key};    # 현재 블록에서만 이 엔트리를 삭제
    local ($cond ? $v1 : $v2);  # 다양한 형태의 L밸류가 지역화를 지원한다

    # 심볼의 지역화

    local *FH;                  # $FH, @FH, %FH, &FH  ... 를 지역화
    local *merlyn = *randal;    # 이제 $merlyn 은 실제로는 $randal 
                                #      @merlyn 은 실제로는 @randal, 등등
    local *merlyn = 'randal';   # 위와 동일: 'randal' 은 *randal 로 승격됨
    local *merlyn = \$randal;   # $merlyn 만 별칭으로 만들고, @merlyn 등 나머지는 아니다

local은 나열된 변수들을 둘러싸고 있는 블록, eval, do 파일에 "지역적"이 되게 하며, 그 블록 내에서 호출하는 다른 서브루틴 내에도 적용되도록 한다.

단순 변수 뿐 아니라 몇 가지 유형의 L밸류들이 지역화될 수 있다:

하나 이상의 변수나 식이 local에 주어질 때는 괄호로 둘러쌀 것

local은 런타임 연산자이므로, 루프를 돌 때마다 실행된다. 따라서 루프 바깥쪽에서 지역화를 하는 게 더 효율적이다.

1.2.3.1. local()의 문법에 관하여 ( Grammatical note on local() )

local은 L밸류 표현식에 적용되는 변경자이며, 지역화된 변수에 할당을 할 때 스칼라로 간주될지 리스트로 간주될지에 영향을 주지 않는다. 따라서

    local($foo) = <STDIN>;
    local @FOO = <STDIN>;
    local $foo = <STDIN>;

1.2.3.2. 특수 변수들의 지역화 ( Localization of special variables )

특수 변수를 지역화할 경우, 새로운 값을 부여하지만 그 변수의 특수한 기능은 사라지지 않는다. 즉 그 기능과 관련된 부수작용(side-effects)들은 지역화된 값에 따라서 동작한다.

    # FILE의 전체 내용을 $slurp에 저장
    { local $/ = undef; $slurp = <FILE>; }

반면 이 특성 때문에 어떤 값들은 지역화하는 데 제한이 있다.

    local $1 = 2;
    sub f { local $_ = "foo"; print }
    for ($1) {
        # 이 지점에서 $_ 는 $1의 별칭이고, $1은 특수 변수이며 읽기전용이다
        f();
    }

경고: tie된 배열과 해쉬를 지역화하는 건 지금까지 설명한 것처럼 동작하지 않는다. 이런 코드를 사용하는 것을 피할 것. (개별 원소를 지역화하는 것은 괜찮음) 이에 관해서는 Perldoc:perl58delta의 "Localising Tied Arrays and Hashes Is Broken" 섹션 참조.

1.2.3.3. glob 지역화 ( Localization of globs )

다음 구문은 현재 패키지에 name glob을 위한 완전히 새로운 심볼 테이블 엔트리를 생성한다:

local *name;

1.2.3.4. 구조형 변수의 원소 지역화 ( Localization of elements of composite types )

구조형 변수(배열 또는 해쉬)의 원소를 지역화할 때,

    %hash = ( 'This' => 'is', 'a' => 'test' );
    @ary  = ( 0..5 );
    {
         local($ary[5]) = 6;
         local($hash{'a'}) = 'drill';
         while (my $e = pop(@ary)) {
             print "$e . . .\n";
             last unless $e > 3;
         }
         if (@ary) {
             $hash{'only a'} = 'test';
             delete $hash{'a'};
         }
    }
    print join(' ', map { "$_ $hash{$_}" } sort keys %hash),".\n";
    print "The array has ",scalar(@ary)," elements: ",
          join(', ', map { defined $_ ? $_ : 'undef' } @ary),"\n";
    6 . . .
    4 . . .
    3 . . .
    This is a test only a test.
    The array has 6 elements: 0, 1, 2, undef, undef, 5

존재하지 않는 원소에 대해 local()을 사용할 경우 어떻게 동작할 것인지는 향후에 변경될 수 있다.

1.2.3.5. 구조형 변수의 원소를 지역화 후 삭제 ( Localized deletion of elements of composite types )

주인장 주: perl 5.12 부터 지원하는 기능으로, 5.10에서는 마지막 예제 코드를 실행하면 설명과 다른 결과가 나옴

delete local $array[$idx] 또는 delete local $hash{$key}를 사용하여 구조형 변수의 원소를 현재 블록에서만 제거하고 블록이 끝난 후 복원할 수 있다.

# 배열의 경우
    do {
        my $val = $array[$idx];
        local  $array[$idx];
        delete $array[$idx];
        $val
    }

# 해쉬의 경우
    do {
        my $val = $hash{key};
        local  $hash{key};
        delete $hash{key};
        $val
    }
    my %hash = (
     a => [ 7, 8, 9 ],
     b => 1,
    );

    {
     my $a = delete local $hash{a};
     # $a 는 [ 7, 8, 9 ]
     # %hash 는 (b => 1)

     {
      my @nums = delete local @$a[0, 2];
      # @nums 은 (7, 9)
      # $a 는 [ undef, 8 ] ($a[2]는 완전히 사라졌고 $a[0]은 undef으로 채워졌다)

      $a[0] = 999; # 이 값은 스코프가 끝나면서 제거될 것이다
     }
     # $a 는 [ 7, 8, 9 ]

    }
    # %hash is back to its original state

1.2.4. L밸류 서브루틴 ( Lvalue subroutines )

경고: L밸류 서브루틴은 실험적인 기능이고, Perl 버전이 변경되면서 구현이 바뀔 수 있음

서브루틴이 수정가능한 값을 반환하게 하는 게 가능하다

    my $val;
    sub canmod : lvalue {
        # return $val; 이건 동작하지 않는다. "return"을 쓰지 말 것
        $val;
    }
    sub nomod {
        $val;
    }

    canmod() = 5;   # $val에 할당된다
    nomod()  = 5;   # 에러

좌변의 서브루틴과 우변의 문맥은, 좌변의 서브루틴을 호출하는 자리를 스칼라로 바꿨을 때 적용되는 것과 같이 적용된다:

    data(2,3) = get_data(3,4);
    (data(2,3)) = get_data(3,4);
    (data(2),data(3)) = get_data(3,4);

L밸류 서브루틴은 실험적인 기능이다

    my $some_array_ref = [];    # 뮤테이터가 보호해 줄까??

    sub set_arr {               # 보통의 뮤테이터
        my $val = shift;
        die("expected array, you supplied ", ref $val)
           unless ref $val eq 'ARRAY';
        $some_array_ref = $val;
    }
    sub set_arr_lv : lvalue {   # L밸류 뮤테이터
        $some_array_ref;
    }

    # set_arr_lv는 다음과 같은 코드를 방지할 수 없다!
    set_arr_lv() = { a => 1 };

1.2.5. 심볼테이블 엔트리 넘겨주기 (typeglobs) ( Passing Symbol Table Entries (typeglobs) )

경고: 이 섹션에 나오는 방법은 오래된 버전의 Perl에서는 참조에 의한 호출을 흉내낼 수 있는 유일한 방법이었다. 최신 버전의 Perl에서도 여전히 동작하긴 하지만, 레퍼런스를 사용하는 새로운 방법이 일반적으로 더 쉽다.

배열 같은 변수의 값이 아니라 이름을 서브루틴에 넘겨주어서, 서브루틴에서 그 변수의 지역화된 복사본이 아니라 원본 자체를15 수정할 수 있도록 하고 싶은 경우

타입글로브를 평가하면, 파일핸들, 포맷, 서브루틴을 포함하여 그 이름을 가진 모든 오브젝트를 나타내는 스칼라값을 생산한다. 타입글로브에 뭔가를 할당하면, 할당된 것을 타입글로브에 언급된 이름이 참조하게 된다.

    sub doubleary {
        local(*someary) = @_;
        foreach $elem (@someary) {
            $elem *= 2;
        }
    }
    doubleary(*foo);
    doubleary(*bar);

스칼라는 원래부터 참조에 의해 전달되므로, 이런 방법을 사용하지 않고 명시적으로 $_[0]등을 참조함으로써 스칼라 인자의 값을 수정할 수 있다. 배열도 배열의 모든 원소를 스칼라로 전달함으로써 그 원소들을 수정할 수 있다. 그러나 배열에 push, pop 등을 수행하거나 배열의 크기를 바꾸기 위해서는 타입글로브 또는 레퍼런스를 사용하여야 한다.

이 방법은 하나의 리스트에 다수의 배열을 담아 전달할 때도 유용하다. 일반적으로는 다수의 배열을 인자로 넘길 경우는 모든 배열의 값들이 하나의 리스트로 합쳐져서 각각의 배열을 분리할 수 없기 때문이다.

타입글로브에 대한 더 자세한 것은 Perldoc:perldata의 "Typeglobs and Filehandles" 섹션 참조

1.2.6. 여전히 local()을 사용해야 하는 경우들 ( When to Still Use local() )

my가 있지만 여전히 local 연산자가 빛을 발하는 세 가지 경우가 있으며, 이 때는 my 대신에 local을 사용해야만 한다

1. 전역 변수, 특히 $_에 임시값을 줄 때

    {
        local @ARGV = ("/etc/motd");
        local $/ = undef;
        local $_ = <>;
        @Fields = split /^\s*=+\s*$/;
    }

2. 지역화된 파일핸들,디렉토리핸들,함수를 생성해야 할 때

    sub ioqueue {
        local  (*READER, *WRITER);    # my가 아니다!
        pipe    (READER,  WRITER) or die "pipe: $!";
        return (*READER, *WRITER);
    }
    ($head, $tail) = ioqueue();
    {
        local *grow = \&shrink; # 이 블록이 끝날 때까지만
        grow();                 # 실제로는 shrink()를 호출한다
        move();                 # 만일 move() 안에서 grow()를 호출한다면 그때도 shrink()가 호출된다
    }
    grow();                     # 진짜 grow()를 호출

3. 배열이나 해쉬의 원소 하나를 임시로 변경하고 싶을 때

    {
        local $SIG{INT} = 'IGNORE';
        funct();                            # 인터럽트 불가능한 상태로 수행
    }
    # 여기서 인터럽트 가능여부가 자동으로 복원된다

1.2.7. 참조에 의한 전달 ( Pass by Reference )

둘 이상의 배열 또는 해쉬를 함수에 전달하거나 함수로부터 반환받으면서 그것들의 속성까지 유지되기를 바란다면 명시적인 참조에 의한 전달을 사용해야 한다.

몇가지 예제

    @tailings = popmany ( \@a, \@b, \@c, \@d );

    sub popmany {
        my $aref;
        my @retlist = ();
        foreach $aref ( @_ ) {
            push @retlist, pop @$aref;
        }
        return @retlist;
    }
    @common = inter( \%foo, \%bar, \%joe );
    sub inter {
        my ($k, $href, %seen); # locals
        foreach $href (@_) {
            while ( $k = each %$href ) {
                $seen{$k}++;
            }
        }
        return grep { $seen{$_} == @_ } keys %seen;
    }

여기까지는 일반적인 리스트 반환 메커니즘을 사용했다. 해쉬를 전달하거나 리턴하고 싶은 경우는 어떻게 할 것인가? 하나의 해쉬만 리턴하거나, 해쉬들이 병합되어도 상관없다면 이런 일반적인 호출 형태도 상관없다.

문제가 되는 경우:

    (@a, @b) = func(@c, @d);
또는
    (%a, %b) = func(%c, %d);

레퍼런스를 사용하여서, 아주 보기 좋진 않더라도 더 깔끔한 코드를 만들 수 있다.

    ($aref, $bref) = func(\@c, \@d);
    print "@$aref has more than @$bref\n";
    sub func {
        my ($cref, $dref) = @_;
        if (@$cref > @$dref) {
            return ($cref, $dref);
        } else {
            return ($dref, $cref);
        }
    }
    (*a, *b) = func(\@c, \@d);
    print "@a has more than @b\n";
    sub func {
        local (*c, *d) = @_;
        if (@c > @d) {
            return (\@c, \@d);
        } else {
            return (\@d, \@c);
        }
    }

파일핸들을 전달하려고 할 때는 *STDOUT처럼 타입글로브를 그대로(bare typeglob) 쓸 수도 있고, 타입글로브의 레퍼런스를 쓸 수도 있다:

    splutter(\*STDOUT);
    sub splutter {
        my $fh = shift;
        print $fh "her um well a hmmm\n";
    }

    $rec = get_rec(\*STDIN);
    sub get_rec {
        my $fh = shift;
        return scalar <$fh>;
    }

새로운 파일핸들을 생성할 경우는 다음과 같이 할 수 있다. 이 때는 레퍼런스가 아니라 있는 그대로의(bare) *FH를 넘기는 것에 유의하라

    sub openit {
        my $path = shift;
        local *FH;
        return open (FH, $path) ? *FH : undef;
    }

주인장 코멘트:

openit()을 호출하는 쪽에서는 *IN = openit()와 같이 파일핸들 타입글로브를 쓸 수도 있는데, 이 경우 openit()이 undef을 반환할 경우 경고가 뜬다.

최신 Perl에서는 파일핸들을 스칼라변수에 담아 쓸 수 있으므로, my $in = openit()와 같이 사용하는 것이 낫지 싶다.

1.2.8. 프로토타입 ( Prototypes )

Perl은 함수 프로토타입을 사용하여 매우 제한된 종류나마 컴파일타임에 인자를 체크하는 것을 지원한다.

    sub mypush (\@@)

프로토타입 기능의 주 목적은 서브루틴을 내장함수들처럼 동작하도록 정의할 수 있게 하는 것이다

    아래와 같이 선언하고       아래와 같이 호출함

    sub mylink ($$)          mylink $old, $new
    sub myvec ($$$)          myvec $var, $offset, 1
    sub myindex ($$;$)       myindex &getstring, "substr"
    sub mysyswrite ($$$;$)   mysyswrite $buf, 0, length($buf) - $off, $off
    sub myreverse (@)        myreverse $a, $b, $c
    sub myjoin ($@)          myjoin ":", $a, $b, $c
    sub mypop (\@)           mypop @array
    sub mysplice (\@$$@)     mysplice @array, 0, 2, @pushme
    sub mykeys (\%)          mykeys %{$hashref}
    sub myopen (*;$)         myopen HANDLE, $name
    sub mypipe (**)          mypipe READHANDLE, WRITEHANDLE
    sub mygrep (&@)          mygrep { /foo/ } $a, $b, $c
    sub myrand (;$)          myrand 42
    sub mytime ()            mytime

백슬래쉬가 붙은 프로토타입 문자들은, 실인자가 정확히 그 문자로 시작해야 함을 의미한다. @_에 담겨 전달되는 값은 그 실인자 앞에 \를 붙여 얻은 레퍼런스가 된다.

    sub myref (\[$@%&*])
    myref $var
    myref @array
    myref %hash
    myref &sub
    myref *glob

백슬래쉬가 붙지 않은 프로토타입 문자들은 특별한 의미를 갖는다

    use Symbol 'qualify_to_ref';

    sub foo (*) {
        my $fh = qualify_to_ref(shift, caller);
        ...
    }

세미콜론(;)은 필수적인 인자와 선택적인 인자를 구분한다.

프로토타입의 가장 마지막 자리 또는 세미콜론 바로 앞자리에 $가 들어갈 경우 그 대신에 _를 쓸 수 있다

위 테이블에서 마지막 세 가지 예제가 구문해석기에 의해 어떻게 해석되는지 유의하라:

    mytime +2;

&와 관련된 흥미로운 것은, 이게 제일 앞자리에 주어질 경우 이것을 사용하여 새로운 문법을 생성할 수 있다는 것이다:

    sub try (&@) {
        my($try,$catch) = @_;
        eval { &$try };
        if ($@) {
            local $_ = $@;
            &$catch;
        }
    }
    sub catch (&) { $_[0] }

    try {
        die "phooey";
    } catch {
        /phooey/ and print "unphooey\n";
    };

다음은 Perl의 grep 연산자를 새로 구현한 코드:

    sub mygrep (&@) {
        my $code = shift;
        my @result;
        foreach $_ (@_) {
            push(@result, $_) if &$code;
        }
        @result;
    }

일부 펄 유저들은 프로토타입에 알파벳과 숫자를 사용한 이름을 쓸 수 있기를 원한다.

기존에 있던 함수에 프로토타입을 도입하여 개선하려 하지 말고, 새로운 함수를 만들어 프로토타입을 적용하는 게 최선일 것이다.

    sub func ($) {
        my $n = shift;
        print "you gave me $n\n";
    }
    func(@foo);
    func( split /:/ );

1.2.9. 상수 함수 ( Constant Functions )

프로토타입이 ()인 함수들은 인라인함수가 될 가능성이 있다.

다음 함수들은 모두 인라인될 것이다:

    sub pi ()           { 3.14159 }             # 정확한 값은 아니고 근사치.
    sub PI ()           { 4 * atan2 1, 1 }      # 최대한 정확하며, 인라인함수가 된다!

    sub ST_DEV ()       { 0 }
    sub ST_INO ()       { 1 }

    sub FLAG_FOO ()     { 1 << 8 }
    sub FLAG_BAR ()     { 1 << 9 }
    sub FLAG_MASK ()    { FLAG_FOO | FLAG_BAR }

    sub OPT_BAZ ()      { not (0x1B58 & FLAG_MASK) }

    sub N () { int(OPT_BAZ) / 3 }

    sub FOO_SET () { 1 if FLAG_MASK & FLAG_FOO }

다음 함수들은 인라인되지 않는다. 더 안쪽 스코프가 있고, 상수폴딩의 결과가 하나의 상수로 나오지 않기 때문이다:

    sub foo_set () { if (FLAG_MASK & FLAG_FOO) { 1 } }

    sub baz_val () {
        if (OPT_BAZ) {
            return 23;
        }
        else {
            return 42;
        }
    }

인라인이 될 가능성이 있는 서브루틴을 재정의할 경우, 필수적인 경고24가 발생한다. (이 경고를 이용해서 특정 서브루틴이 상수로 간주되는지 여부를 확인할 수 있다)

    sub not_inlined () {
        23 if $];
    }

1.2.10. 내장 함수 오버라이드 ( Overriding Built-in Functions )

많은 내장 함수들을 오버라이드할 수 있다.

오버라이딩은 컴파일 타임에 함수를 모듈로부터 임포트하는 형태로 이뤄진다 -- 단지 호출 전 미리 선언하는 것만으로는 충분하지 못하다.

    use subs 'chdir', 'chroot', 'chmod', 'chown';
    chdir $somewhere;
    sub chdir { ... }

기존 내장함수를 참조하려는 의도를 명백하게 나타내려면, 내장 함수의 이름 앞에 특수한 패키지 한정자인 CORE::를 붙인다.

일반적으로 라이브러리 모듈들은 open이나 chdir과 같은 내장 함수의 이름을 기본 @EXPORT 리스트에 포함해서 익스포트하지 말아야 한다.

    use Module 'open';
    use Module;

위의 메커니즘은 임포트를 요청한 패키지 내에서만 오버라이드가 이뤄진다. 만일 네임스페이스의 경계를 넘어서 모든 곳에서 내장함수를 오버라이드하고자 하길 원한다면, 서브루틴을 특별한 네임스페이스인 CORE::GLOBAL:: 안으로 임포트한다.

    package REGlob;
    require Exporter;
    @ISA = 'Exporter';
    @EXPORT_OK = 'glob';

    sub import {
        my $pkg = shift;
        return unless @_;
        my $sym = shift;
        my $where = ($sym =~ s/^GLOBAL_// ? 'CORE::GLOBAL' : caller(0));
        $pkg->export($where, $sym, @_);
    }

    sub glob {
        my $pat = shift;
        my @got;
        if (opendir my $d, '.') {
            @got = grep /$pat/, readdir $d;
            closedir $d;
        }
        return @got;
    }
    1;
    #use REGlob 'GLOBAL_glob';      # 모든 네임스페이스에서 glob()을 오버라이드하는 경우
    package Foo;
    use REGlob 'glob';              # Foo:: 안에서만 glob()을 오버라이드
    print for <^[a-z_]+\.pm\$>;     # 이름이 소문자로만 된 프라그마 모듈들을 출력

내장 함수를 오버라이드할 경우, 대체되는 함수도 가능하면 내장 함수의 문법을 그대로 쓸 수 있게 작성해야 한다.

어떤 내장 함수(system이나 chomp 등)는 사용 문법을 프로토타입으로 표현할 수 없다. 이런 함수를 오버라이드할 경우는 그 함수의 원래의 문법을 완벽하게 모방할 수 없을 것이다.

내장 do, require, glob은 오버라이드 할 수는 있으나, 그 함수들의 특수한 기능들 때문에, 함수의 문법이 보존된다. 따라서 오버라이드하는 대체함수를 위한 프로토타입을 정의할 필요가 없다. (다만 do BLOCK 문법을 오버라이드하는 것을 불가능하다)

require는 이 외에도 특별한 기능이 있다: require Foo::Bar의 형태로 오버라이드한 함수를 호출하면, 그 함수에 @_에 담겨서 실제로 전달되는 인자는 "Foo/Bar.pm"이다. Perldoc:perlfunc의 "require"를 참조하라.

위에서 보았듯이 glob을 오버라이드하면 글롭 연산자 <*>도 같이 오버라이드된다.

비슷하게, readline 함수를 오버라이드하면 이와 동등한 I/O연산자 <파일핸들>도 오버라이드된다. 또한 readpipe을 오버라이드하면 ``qw// 연산자도 오버라이드된다.

끝으로, 일부 내장함수(exists 또는 grep 등)은 오버라이드할 수 없다.

1.2.11. 자동 로드 ( Autoloading )

정의되지 않은 서브루틴을 호출할 경우 곧바로 치명적 에러가 발생하며 종료된다. (메쏘드로 사용되는 서브루틴의 경우 클래스 패키지와 그 상위클래스 어디에도 그 메쏘드가 없는 경우도 마찬가지)

많은 경우 AUTOLOAD 루틴은 eval()을 사용하여 원래 요청된 서브루틴의 정의를 로드한 후, AUTOLOAD의 스택프레임을 삭제할 수 있는 특별한 형태의 goto()를 사용하여 그 서브루틴을 실행한다. (예를 들어 Perldoc:AutoLoader 에 문서화되어 있는 표준 모듈의 소스를 참조하라) 하지만 단순히 원래의 서브루틴의 흉내만 내고 전혀 정의하지 않을 수도 있다.

    sub AUTOLOAD {
        my $program = $AUTOLOAD;
        $program =~ s/.*:://;
        system($program, @_);
    }
    date();
    who('am', 'i');
    ls('-l');
    use subs qw(date who ls);
    date;
    who "am", "i";
    ls '-l';

더 복잡한 예는 표준 Perldoc:Shell에서 볼 수 있다, 여기서는 존재하지 않는 서브루틴 호출은 외부 프로그램을 호출하는 것으로 취급된다.

이런 자동 로드 메커니즘은 모듈 작성자가 자신의 모듈을 자동으로 로드할 수 있는 파일들로 분리하는 것을 돕기 위해 존재한다. Perldoc:AutoLoader, Perldoc:AutoSplit, Perldoc:SelfLoader, Perldoc:perlxs 등을 참조하라.

1.2.12. 서브루틴 속성 ( Subroutine Attributes )

이 섹션의 내용은 주인장도 잘 모르는 부분이라, 번역에 오류가 있을 가능성이 높습니다

서브루틴의 선언이나 정의에는 그 서브루틴과 연관된 속성을 나열해 줄 수 있다.

각 속성의 명칭들은 간단한 식별자로서 사용할 수 있는 형태여야 한다 ('_' 문자를 제외하고는 구두점을 쓸 수 없다). 각 속성에는 파라메터 리스트가 뒤에 붙을 수 있으며, 괄호가 정확하게 중첩되었는지만 체크된다.

올바른 문법의 예: (실제 이런 이름의 속성 자체는 존재하지 않지만)

    sub fnord (&\%) : switch(10,foo(7,3))  :  expensive;
    sub plugh () : Ugly('\(") :Bad;
    sub xyzzy : _5x5 { ... }

올바르지 않은 문법의 예:

    sub fnord : switch(10,foo();  # 괄호의 좌우 쌍이 맞지 않음
    sub snoid : Ugly('(');        # 괄호의 좌우 쌍이 맞지 않음
    sub xyzzy : 5x5;              # "5x5"는 올바른 식별자가 아님
    sub plugh : Y2::north;        # "Y2::north"는 간단한 식별자가 아님
    sub snurt : foo + bar;        # "+"는 콜론도 공백도 아님

속성 리스트는 그 서브루틴과 속성을 연결시키는 코드에 상수 문자열 리스트 형태로 전달된다. 특히 위의 두번째 예문은 다음과 같이 해석되고 수행된다:

    use attributes __PACKAGE__, \&plugh, q[Ugly('\(")], 'Bad';

속성 목록의 더 자세한 내용과 사용법에 대해서는 Perldoc:attributesPerldoc:Attribute::Handlers 참조

1.3. 참고

2. 다른 문서들에서

2.1. 선언 순서

서브루틴 정의와 사용의 순서25
sub setup;         # 이름만 선언
# 또는
use subs qw(setup get_input 등등);

setup;             # 사용
sub setup { ... }  # 실제 정의

동일한 이름의 서브루틴이 둘 이상 있으면, 나중의 것이 적용된다.26

Perl의 built-in function 과 동일한 이름의 서브루틴을 호출하는 경우는 반드시 "&"가 필요하다. 이런 이유로, Learning Perl에서는 "Perl의 모든 내장 함수의 이름을 알고 있지 않다면" 항상 "&"를 붙일 것을 권장한다.27

그러나 최근 경향은 "&"를 앞에 붙이지 않는 것이다.28

2.2. Context

wantarray29

주인장이 네이버 까페에 쓴 [다양한 상황에서 컨텍스트 확인]도 참고할 것:

sub foo {
    my $wa = wantarray;

    if ( not defined $wa ) {        # 정의되지 않는다면 void 문맥
        print "void context\n";
    }
    elsif ( $wa ) {                 # 값이 참이면 list 문맥
        print "list context\n";
    }
    else {                          # 정의되었으나 거짓이면 scalar 문맥
        print "scalar context\n";
    }

    return 3;       # 어쨌거나 리턴값은 3
}

my $scalar;
my @array;

foo();                          # void

( foo() );                      # void

()          = foo();            # list

$scalar     = foo();            # scalar
print "scalar = [$scalar]\n";   # 3

( $scalar ) = foo();            # list
print "scalar = [$scalar]\n";   # 3

$scalar     = ( foo() );        # scalar !!
print "scalar = [$scalar]\n";   # 3

$scalar     = ( () = foo() );   # list
print "scalar = [$scalar]\n";   # 1 !!

@array      = foo();            # list
print "array  = ", join(":", @array), "\n"; # ( 3 )

$array[foo()] = 10;             # scalar
print "array  = ", join(":", @array), "\n"; # ( 3, undef, undef, 10 )

@array[foo()] = 20;             # list
print "array  = ", join(":", @array), "\n"; # ( 3, undef, undef, 20 )

2.3. Prototype

파라메터의 갯수와 종류를 명시할 수 있다.30
sub sum_of_two_squares ($$);

print sum_of_two_squares($first, $second),"\n";

# print sum_of_two_squares($first, $second, 0),"\n";  <- 이건 에러

sub sum_of_two_squares ($$) {
   my ($a,$b) = (shift, shift);
   return $a**2+$b**2;
}

2.4. 인자로 레퍼런스 넘겨주기

호출하는 쪽에서 레퍼런스를 명시적으로 넘길 수도 있고
my $a = 5;
increment(\$a);
프로토타입에 "\"+"변수타입심볼 형태로 적어주면 자동으로 레퍼런스를 취한다
sub increment(\$);
my $a = 5;
increment($a);
sub increment(\$) {
   my $reference = shift;
   $$reference++;
}

2.5. 인자로 파일핸들 넘겨주기

/파일 참조

2.6. Named parameter

해쉬 형태로 인자 넘겨주기 - 어차피 받는 쪽은 @_로 받음.
logon(username => $name, password => $pass, host => $hostname);

sub logon {
   die "Parameters to logon should be even" if @_ % 2;
   my %args = @_; # 넘어온 인자로 해쉬 구성
   print "Logging on to host $args{hostname}\n";
   ...
}

2.7. 서브루틴의 레퍼런스

선언
sub something { print "Wibble!\n" }
my $ref = \&something;

# 익명 서브루틴
my $ref = sub { print "Wibble!\n" };

호출

&{$ref};
&{$ref}(@parameters);
&$ref(@parameters);

# 아래의 형태를 권장
$ref->();
$ref->(@parameters);

2.8. Closure

스코프를 벗어난 렉시컬 변수를 참조하는 서브루틴31

콜백 함수의 예:32

use File::Find;

# 현재 디렉토리 이하의 파일들 모두의 크기의 합을 구함
my $total_size = 0;
# File::Find 모듈은 $total_size에 접근할 수 없지만,
# 그 모듈이 콜백 함수로 호출하는 익명 서브루틴은 $total_size에 접근할 수 있다.
find(sub { $total_size += -s if -f }, '.');
print $total_size, "\n";

클로져 안에서 접근하는 변수들은, 그 서브루틴의 레퍼런스가 존재하는 한 그 변수들도 계속 유지된다.33

use File::Find;

my $callback;
{
  my $count = 0;
  $callback = sub { print ++$count, ": $File::Find::name\n" };
}
# 이 시점에서 $count는 스코프를 벗어나지만, (따라서 접근할 수는 없지만)
# $callback이 레퍼런스 하는 익명 서브루틴에 의해 참조되므로 레퍼런스 카운트가 0이 아니고,
# 저 익명 서브루틴의 레퍼런스가 존재하는 한 메모리에 여전히 존재한다.
find($callback, '.');

2.9. 서브루틴을 반환하는 서브루틴

서브루틴의 레퍼런스를 반환하는 서브루틴34
use File::Find;

sub create_find_callback_that_counts {
  my $count = 0;
  return sub { print ++$count, ": $File::Find::name\n" };
}

my $callback = create_find_callback_that_counts(  );
# bin/ 아래의 파일들은 1번부터 번호가 부여되고
print "my bin:\n";
find($callback, 'bin');
# lib/ 아래의 파일들은 bin/ 파일의 마지막 번호 다음부터 번호가 부여된다.
# 동일한 $count를 사용하기 때문
print "my lib:\n";
find($callback, 'lib');

# 아래의 경우는, 서로 다른 $count 인스턴스를 사용하기 때문에 각각 번호가 1부터 부여된다.
my $callback1 = create_find_callback_that_counts(  );
my $callback2 = create_find_callback_that_counts(  );
print "my bin:\n";
find($callback1, 'bin');
print "my lib:\n";
find($callback2, 'lib');

둘 이상의 서브루틴을 반환하는 예:35

use File::Find;

sub create_find_callbacks_that_sum_the_size {
  my $total_size = 0;
  # 두 개의 익명 서브루틴이 $total_size를 공유
  return(sub { $total_size += -s if -f }, sub { return $total_size });
}

## set up the subroutines
my %subs;
foreach my $dir (qw(bin lib man)) {
  my ($callback, $getter) = create_find_callbacks_that_sum_the_size(  );
# bin, lib, man 세 디렉토리에 대하여 각각 별개의 콜백 서브루틴과 getter가 생성된다.
  $subs{$dir}{CALLBACK}   = $callback;
  $subs{$dir}{GETTER}     = $getter;
}

## gather the data
for (keys %subs) {
  find($subs{$_}{CALLBACK}, $_);
}

## show the data
for (sort keys %subs) {
  my $sum = $subs{$_}{GETTER}->(  );
  print "$_ has $sum bytes\n";
}

2.10. closure variable를 static local variable로 쓰기

named subroutine에서 렉시컬 변수의 레퍼런스를 유지하는 경우36
{ # naked block 안에서
  my $count;
  sub count_one { ++$count }
  sub count_so_far { return $count }
}
# 이 시점에서 $count는 스코프를 벗어났기 때문에 직접 접근할 수는 없지만
# count_one과 count_so_far는 closure이고 블록 밖에서도 유지가 되므로
# 그 안에서는 계속 $count를 사용 가능하다

count_one(  );
count_one(  );
count_one(  );
print 'we have seen ', count_so_far(  ), " coconuts!\n";  # 3 coconuts

초기값을 따로 주어야 하는 경우는 주의37

{
# 이 블록은 count_down()을 호출하는 시점보다 먼저 나와야 한다.
# countdown의 초기값을 먼저 10으로 만들어주어야 하기 때문
  my $countdown = 10;
  sub count_down { $countdown-- }
  sub count_remaining { $countdown }
}

count_down(  );
count_down(  );
count_down(  );
print 'we're down to ', count_remaining(  ), " coconuts!\n";
아니면 해당 블록을 BEGIN 블록으로 지정
BEGIN {
  my $countdown = 10;
  sub count_down { $countdown-- }
  sub count_remaining { $countdown }
}

2.11. 서브루틴을 인자로 넘기기

물론 서브루틴의 레퍼런스를 인자로 넘길 수도 있지만...

Perldoc:map, Perldoc:grep, Perldoc:sort 등과 같이 인라인 블록으로 전달하고 싶은 경우38

use List::Util;
# reduce 라는 서브루틴은 인자로 서브루틴과 리스트를 받아서, 서브루틴을 사용하여 리스트의 원소들로부터 스칼라 하나를 생성한다.
my $sum = reduce { $a + $b } @list;   # 모든 원소의 합을 구하는 경우


# reduce 의 구현
package List::Util;

sub reduce (&@) {
    my $code = shift;
    no strict 'refs';

    return shift unless @_ > 1;

    use vars qw($a $b);

    my $caller = caller;

    # reduce 를 호출한 쪽에서 바라보는 $a와 $b는 호출한 쪽 패키지의 패키지 변수이다.
    # reduce 안에서 사용하는 $a와 $b는 렉시컬 변수이다.
    # 따라서 이 둘을 서로 alias 해주어야 함

    # local()을 해 주지 않으면 호출한 쪽의 패키지 변수 $a, $b 가 계속 여기에 alias되어 있어서 곤란하다.
    local(*{$caller."::a"}) = \my $a;
    local(*{$caller."::b"}) = \my $b;

    $a = shift;
    foreach (@_) {
        $b = $_;
        $a = &{$code}();
        print "now a = $a, b = $b\n";
    }

    $a;
}

3. 기타

4. Comments

이름:  
Homepage:
내용:
 


컴퓨터분류
각주:
1. Pascal 등
2. C 등에서 함수 정의할 때 foo(int i, char c)에서 i와 c같은 것들
3. 주인장 생각으로는 아마도 호출되는 서브루틴을 위해서 다시 @_ 변수를 세팅하지 않아도 되기 때문이 아닐까 싶다
4. 즉 패키지(전역) 변수를 인덱스 변수로 사용하더라도 루프가 끝나면 원래의 값으로 되돌아감
5. 7. $패키지이름::변수이름의 형태, 예: $main::str
6. Perl/Closure도 참조
8. perl -e "스크립트"의 -e 대신 -E 를 사용하라는 이야기
9. Strawberry Perl 5.10 에서는 배열 변수에 state를 선언하니 에러가 났음
10. static 으로 선언된 전역 변수
11. input record seperator, $/
12. 배열의 경우 3번 인덱스와 4번 인덱스에 해당하는 원소가 블록 내의 pop에 의해 제거되었다가, 5번 인덱스의 원소가 원래의 값으로 복원되면서 중간의 두 원소는 undef 값으로 채워졌다. 해쉬에서는 'a'키에 해당하는 원소가 지역화된 후 delete에 의해 지워졌다가 블록이 끝나면서 복원되었다
13. delete local *** 형태로 쓸 경우는 local의 스코프가 이 구문이 있는 블록이니까, do {} 블록보다 하나 바깥쪽 블록에 해당된다
14. setter 메쏘드
15. 원문에는 global copy of it 이라고 나오는데 전역화된 복사본이라는 표현도 이상해서...
16. 마지막 문장은 "Look out for implicit assignments in while conditionals."인데 뜻이 애매하다. while (<STDIN>) { } 형태의 경우 일단 $_가 자동으로 로컬라이즈되지 않는다. 따라서 그런 곳에서 명시적으로 while (local $_ = <STDIN>)처럼 로컬라이즈를 해주는 것이 좋다는 뜻인 것 같기도 한데, 그럴 거면 차라리 my $_ = ...처럼 my를 쓰는 게 나을 것이다. 아니면 이런 루프 안에서 다시 $_를 변경하는 구문을 넣을 경우 local을 써 주는 게 필수적이라는 뜻 같기도 한데, 어쨌거나 주인장은 정확한 해석을 못 하겠음
17. 원문은 "dynamics". 정확히는 동적 스코프의 영향을 받는 전역변수를 대상으로라는 의미인 듯
18. 예를 들어 use로 불러온 모듈 내에 있는 함수 선언의 프로토타입은 체크가 되지만 require로 불러오면 안 된다
19. 객체지향 방식으로 작성된 코드에서
20. grep { 코드 } 리스트에서 { 코드 }가 이렇게 익명 서브루틴을 넘기는 것이다
21. 주인장 생각에는, 나머지 인자를 전부 취하는 게 문제가 아니라 '빈 리스트'를 인자로 받아도 문제가 없는 게 핵심인 듯하다. 예를 들어 프로토타입이 $$$면 반드시 스칼라 3개를 인자로 받아야 하고 $$;$면 2개 또는 3개를 받을 수 있다. 하지만 프로토타입이 $$@인 경우는 스칼라 2개만 인자로 넘겨받아도 에러가 나지 않고 마지막 @는 빈 리스트로 간주된다. 따라서 굳이 $$;@라고 쓸 필요가 없다
22. 이건 고쳐질 일이 없다는 얘기려나... 세상을 떠도는 구버전 Perl 코드들을 언제 다 살펴본다고..?
23. constant folding, 효율성을 위해서 상수들로 표현된 식을 미리 평가하여 상수 결과로 치환해 놓는 것
24. 원문은 mandatory warning. use warnings 프라그마를 사용하지 않아도 경고가 발생함
25. 29. 30. Beginning Perl, Chapter 08
26. 27. Learning Perl, Chapter 04
28. [Subroutines called with the ampersand / Perl 5 Wiki], [거침없이 배우는 펄(learning perl 5th 한글번역판) 비평 - perl_docs - GitHub]
31. 32. 33. 34. 35. 36. 37. Intermediate Perl, Chapter 07
38. Mastering Perl, Chapter 09

마지막 편집일: 2019-10-2 3:34 pm (변경사항 [d])
7086 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기