서브루틴 선언하기
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; # 현재 @_ 변수를 호출된 서브루틴에서 사용할 수 있게 함
사용자가 정의하는 서브루틴:
do
, require
, use
를 사용하여 다른 파일에서 로드할 수 있다
eval
또는 익명 서브루틴을 사용하여 실행 시간에 생성할 수 있다
Perl에서의 함수 호출과 리턴값 모델:
넘겨진 인자들은 @_
배열을 통해 접근할 수 있다.
$_[0]
, $_[1]
에 담긴다
@_
배열 자체는 지역 변수이지만, 이 배열의 원소들은 실제 스칼라 실인자들의 별칭(alias)으로 되어 있다. $_[0]
원소를 변경하면, 대응되는 실인자도 변경된다 (만일 실인자가 변경 불가능한 것이라면 에러가 난다).
@_
배열 전체에 새로운 값을 할당하면 별칭이 제거되며, 인자들은 변경되지 않는다.
return
구문:
return
구문이 없고, 마지막 구문이 표현식인 경우 그 식의 값이 반환된다.
foreach
, while
같은 루프 제어 구조인 경우는 반환값이 명세되어 있지 않다
Perl에는 이름 있는 형식 인자2를 지원하지 않는다.
my()
리스트에 할당해서 사용한다.
예:
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"를 참조
서브루틴을 호출할 때 명시적으로 &
접두어를 붙일 수 있다.
&
는 생략가능하다
&
가 필수적인 경우:
&$subref()
또는 &{$subref}()
구조를 사용하여 이름이나 레퍼런스를 통한 간접 호출을 하는 경우. 이 것은 $subref->()
표기법을 사용하여 피할 수 있다.
서브루틴이 재귀적으로 호출되는 경우:
&
를 붙인 형태로 호출할 경우, 인자 리스트를 생략할 수 있으며, 생략하면 호출하는 시점의 @_
배열이 서브루틴에 보이게 된다. 이것은 효율적이지만3 초심자는 피하고 싶을 수 있다:
&foo(1,2,3); # 인자 세 개를 전달 foo(1,2,3); # 위와 동 foo(); # 빈 리스트를 전달 &foo(); # 위와 동 &foo; # foo()는 현재 인자목록을 그대로 취한다, foo(@_)와 동일하다 !! foo; # 서브루틴이 미리 선언된 경우에는(또한 그 경우에만) foo()와 동일하다 # 미리 선언되지 않았다면 bareword "foo"이다.
&
를 붙인 형태는 인자 리스트를 생략가능하게 만들어주는 것 이외에도, 프로토타입 체크를 하지 않도록 하는 기능도 있다
이름이 대문자로만 구성된 서브루틴
AUTOLOAD
, CLONE
, DESTROY
, perltie PerlIO::via 에 언급되는 함수들.
BEGIN
, UNITCHECK
, CHECK
, INIT
, END
my $foo; # $foo를 렉시컬 지역 변수로 선언 my (@wid, %get); # 변수들의 리스트를 지역 변수로 선언 my $foo = "flurp"; # $foo를 렉시컬로 선언하고 초기화 my @oof = @bar; # @oof를 렉시컬로 선언하고 초기화 my $x : Foo = $y; # 속성이 명시됨
경고: my 선언에서 속성 리스트를 사용하는 것은 개발 중인 기능이며, 시맨틱이나 인터페이스가 차후에 변경될 수 있다. attributes , Attribute::Handlers 참조.
my
연산자
eval
$/
등과 같은 빌트인 변수들은 local
키워드를 사용하여 지역화되어야 한다.
local
연산자를 사용하여 생성된 동적 변수와 달리, my
를 사용하여 선언한 렉시컬 변수는 호출된 서브루틴을 비롯한 외부로부터 완전히 감추어진다.
어떤 지점을 정적으로 둘러싸고 있는 스코프 내에서 선언된 my
변수는 볼 수 있다. 오직 동적 스코프만 차단된다.
bumpx()
함수는 렉시컬 $x 변수에 접근할 수 있다. my
와 sub
가 동일한 스코프에 나타나기 때문이다:
my $x = 10; sub bumpx { $x++ }
그러나 eval()
구문은 이 구문이 평가되는 스코프 내에 있는 렉시컬 변수를, eval() 자신 내부에서 다시 선언하면서 변수의 이름을 감춰버리지 않는 이상은, 접근할 수 있다. 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
제어문의 경우는 실행되는 블록 뿐 아니라 조건식이 있는 부분도 같은 스코프의 일부이다.
continue
절까지 포함한 루프 전체:
while (my $line = <>) { $line = lc $line; } continue { print $line; }
elsif
와 else
를 전부 포함한 조건문 전체:
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
루프의 경우
local
을 사용한 것처럼 스코프를 동적으로 조정한다4
my
로 선언되거나, 이미 그 이름의 렉시컬 변수가 존재할 경우는, 새로운 렉시컬 변수를 생성하여 사용한다.
some_function()
안에서는$i에 접근할 수 없다
for my $i (1, 2, 3) { some_function(); }
렉시컬 변수를 사용하는 걸 장려
use strict 'vars';
our
또는 use vars
를 사용하여 미리 선언되었거나, 이름에 패키지 이름까지 포함한 형태로5 적어주어야 한다.
no strict 'vars'
를 하여 이 제한을 다시 없앨 수 있다.
my
의 컴파일타임과 런타임 효과
my
로 선언한 변수는 어느 패키지에도 속하지 않으므로, "fully qualified name"7을 쓸 수 없다.
my $pack::var; # 에러! 허용되지 않는 문법
::
를 사용한 fully qualified 표기법을 써서 접근할 수 있다:
package main; local $x = 10; my $x = 20; print "$x and $::x\n"; # "20 and 10" 출력
my 변수를 파일의 가장 바깥쪽 스코프에서 선언하여, 파일 외부의 동일한 식별자를 가진 변수들을 숨길 수 있다.
$some_pack::secret_version
과 같은 식으로 접근할 수 없다는 걸 상기하라.
my $secret_version = '1.001-beta'; my $secret_sub = sub { print $secret_version }; &$secret_sub();
영구적으로 값이 보존되는(persistent) 변수를 만드는 데는
state
사용
my
가 들어갈 자리에 대신 state
키워드 사용
feature
프라그마를 쓰거나, 원-라이너로 실행할 때는 -E
를 사용하라8. feature 참조.
use feature 'state'; sub gimme_another { state $x; return ++$x }
$x
도 렉시컬 변수이고, 이 서브루틴 밖에서는 접근할 수 없다.
state $x=42
와 같이) 이 할당은 처음 한 번만 실행된다. 이후에는 무시된다. 스칼라가 아닌 변수의 경우에 어떻게 동작하는지는 정의되지 않았다.9
렉시컬 변수는 C 언어에서 auto 변수와 비슷하나,
C에서 함수 내 정적(static) 변수와 같은 효과를 내도록 하는 법
{ my $secret_val = 0; sub gimme_another { return ++$secret_val; } } # $secret_val은 블록 바깥쪽에서는 접근할 수 없다 # 그러나 gimme_another의 매 호출 사이에도 값이 계속 유지된다
require
나 use
를 통해 불릴 때는 상관이 없는데, 메인 프로그램 내에 있을 경우는 이 함수를 호출하기 전에 my
선언이 먼저 실행되도록 배치해야 한다. 이 블록 전체를 메인 프로그램 앞부분에 두거나, BEGIN
블록을 사용한다, perlmod의 "BEGIN, UNITCHECK, CHECK, INIT and END" 섹션 참조
BEGIN { my $secret_val = 0; sub gimme_another { return ++$secret_val; } }
가장 바깥쪽 스코프(파일 스코프)에서 선언된 렉시컬 변수는 C 언어에서 파일 스태틱10와 유사하게 동작한다.
경고: 일반적으로는 local
대신 my
를 사용하라, 그것이 더 빠르고 안전하다.
local
이 많이 사용됨
# 값의 지역화 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 파일
에 "지역적"이 되게 하며, 그 블록 내에서 호출하는 다른 서브루틴 내에도 적용되도록 한다.
my
를 사용하여 하며, C의 auto 선언과 유사하게 동작한다.
단순 변수 뿐 아니라 몇 가지 유형의 L밸류들이 지역화될 수 있다:
하나 이상의 변수나 식이 local
에 주어질 때는 괄호로 둘러쌀 것
local
은 런타임 연산자이므로, 루프를 돌 때마다 실행된다. 따라서 루프 바깥쪽에서 지역화를 하는 게 더 효율적이다.
local
은 L밸류 표현식에 적용되는 변경자이며, 지역화된 변수에 할당을 할 때 스칼라로 간주될지 리스트로 간주될지에 영향을 주지 않는다. 따라서
local($foo) = <STDIN>; local @FOO = <STDIN>;
local $foo = <STDIN>;
특수 변수를 지역화할 경우, 새로운 값을 부여하지만 그 변수의 특수한 기능은 사라지지 않는다. 즉 그 기능과 관련된 부수작용(side-effects)들은 지역화된 값에 따라서 동작한다.
# FILE의 전체 내용을 $slurp에 저장 { local $/ = undef; $slurp = <FILE>; }
반면 이 특성 때문에 어떤 값들은 지역화하는 데 제한이 있다.
local $1 = 2;
sub f { local $_ = "foo"; print } for ($1) { # 이 지점에서 $_ 는 $1의 별칭이고, $1은 특수 변수이며 읽기전용이다 f(); }
경고: tie된 배열과 해쉬를 지역화하는 건 지금까지 설명한 것처럼 동작하지 않는다. 이런 코드를 사용하는 것을 피할 것. (개별 원소를 지역화하는 것은 괜찮음) 이에 관해서는 perl58delta의 "Localising Tied Arrays and Hashes Is Broken" 섹션 참조.
다음 구문은 현재 패키지에 name
glob을 위한 완전히 새로운 심볼 테이블 엔트리를 생성한다:
local *name;
name
파일핸들)가 동적으로 리셋된다
local */
는 "입력 레코드 구분자"11의 내부 값에 영향을 미치지 않는다.
local $_
대신에 local */
를 사용해야 한다.
my $_
로 선언한다)를 써서 이 문제를 완전히 피할 수 있다.
구조형 변수(배열 또는 해쉬)의 원소를 지역화할 때,
local()
이 적용되는 스코프가 끝났을 때, local()
에서 명명했던 이름을 키값으로 하는 해쉬원소의 기존 값이 복원된다. 배열의 경우는 명명됐던 인덱스에 해당하는 원소의 값이 복원된다.
local()
이 적용된 상태에서 원소가 제거되었을 경우(해쉬에서 delete()
를 쓰거나 배열에shift
를 하는 경우처럼), 복원될 때 배열이 확장될 수 있고 이 때 중간에 끼인 원소들은 undef 값을 갖게 된다.
%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()을 사용할 경우 어떻게 동작할 것인지는 향후에 변경될 수 있다.
주인장 주: perl 5.12 부터 지원하는 기능으로, 5.10에서는 마지막 예제 코드를 실행하면 설명과 다른 결과가 나옴
delete local $array[$idx]
또는 delete local $hash{$key}
를 사용하여 구조형 변수의 원소를 현재 블록에서만 제거하고 블록이 끝난 후 복원할 수 있다.
local
의 스코프가 do
블록으로 한정된다는 것이 다르다13:
# 배열의 경우 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
경고: 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 };
경고: 이 섹션에 나오는 방법은 오래된 버전의 Perl에서는 참조에 의한 호출을 흉내낼 수 있는 유일한 방법이었다. 최신 버전의 Perl에서도 여전히 동작하긴 하지만, 레퍼런스를 사용하는 새로운 방법이 일반적으로 더 쉽다.
배열 같은 변수의 값이 아니라 이름을 서브루틴에 넘겨주어서, 서브루틴에서 그 변수의 지역화된 복사본이 아니라 원본 자체를15 수정할 수 있도록 하고 싶은 경우
*foo
처럼, 이름 앞에 별표를 붙여서 그 이름에 해당되는 모든 오브젝트를 참조할 수 있다.
타입글로브를 평가하면, 파일핸들, 포맷, 서브루틴을 포함하여 그 이름을 가진 모든 오브젝트를 나타내는 스칼라값을 생산한다. 타입글로브에 뭔가를 할당하면, 할당된 것을 타입글로브에 언급된 이름이 참조하게 된다.
sub doubleary { local(*someary) = @_; foreach $elem (@someary) { $elem *= 2; } } doubleary(*foo); doubleary(*bar);
스칼라는 원래부터 참조에 의해 전달되므로, 이런 방법을 사용하지 않고 명시적으로 $_[0]
등을 참조함으로써 스칼라 인자의 값을 수정할 수 있다. 배열도 배열의 모든 원소를 스칼라로 전달함으로써 그 원소들을 수정할 수 있다. 그러나 배열에 push
, pop
등을 수행하거나 배열의 크기를 바꾸기 위해서는 타입글로브 또는 레퍼런스를 사용하여야 한다.
이 방법은 하나의 리스트에 다수의 배열을 담아 전달할 때도 유용하다. 일반적으로는 다수의 배열을 인자로 넘길 경우는 모든 배열의 값들이 하나의 리스트로 합쳐져서 각각의 배열을 분리할 수 없기 때문이다.
타입글로브에 대한 더 자세한 것은 perldata의 "Typeglobs and Filehandles" 섹션 참조
my
가 있지만 여전히 local
연산자가 빛을 발하는 세 가지 경우가 있으며, 이 때는 my 대신에 local을 사용해야만 한다
1. 전역 변수, 특히 $_에 임시값을 줄 때
@ARGV
나 특수문자변수들은 local()
을 사용하여 지역화하여야 한다.
{ local @ARGV = ("/etc/motd"); local $/ = undef; local $_ = <>; @Fields = split /^\s*=+\s*$/; }
while
조건문 안에서는 묵시적으로 할당하는 곳을 살펴보라16
2. 지역화된 파일핸들,디렉토리핸들,함수를 생성해야 할 때
local()
을 사용하여야 한다. 이것은 새로운 심볼 테이블 엔트리를 생성할 때 사용할 수 있다:
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(); # 인터럽트 불가능한 상태로 수행 } # 여기서 인터럽트 가능여부가 자동으로 복원된다
둘 이상의 배열 또는 해쉬를 함수에 전달하거나 함수로부터 반환받으면서 그것들의 속성까지 유지되기를 바란다면 명시적인 참조에 의한 전달을 사용해야 한다.
몇가지 예제
pop
을 수행한 후 그렇게 뽑아낸 원소들의 리스트를 반환:
@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);
@a
와 %a
에 모든 리턴값이 들어가고 @b
와 %b
는 깨끗이 비게 된다. 또한 함수는 두 개의 분리된 배열이나 해쉬를 전달받을 수 없고, 항상 하나의 긴 리스트 @_
를 받는다.
레퍼런스를 사용하여서, 아주 보기 좋진 않더라도 더 깔끔한 코드를 만들 수 있다.
($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); } }
my
변수들을 사용하고 있다면 제대로 동작하지 않는다. 심볼 테이블에는 전역 변수(local
로 지역화한 경우라도)만 들어있기 때문이다.
파일핸들을 전달하려고 할 때는 *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()
와 같이 사용하는 것이 낫지 싶다.
Perl은 함수 프로토타입을 사용하여 매우 제한된 종류나마 컴파일타임에 인자를 체크하는 것을 지원한다.
mypush
는 push()
와 똑같은 형태의 인자를 받는다:
sub mypush (\@@)
&
를 함수명 앞에 쓰지 않는 스타일로 함수를 호출할 때만 영향을 끼친다.
\&foo
와 같은 서브루틴 레퍼런스나 &{$subref}
또는 $subref->()
와 같은 간접 호출에는 적용되지 않는다.
프로토타입 기능의 주 목적은 서브루틴을 내장함수들처럼 동작하도록 정의할 수 있게 하는 것이다
아래와 같이 선언하고 아래와 같이 호출함 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
백슬래쉬가 붙지 않은 프로토타입 문자들은 특별한 의미를 갖는다
@
와 %
는 남아있는 인자 전부를 모아서 사용하고, 리스트 문맥을 강제한다
$
는 스칼라 문맥을 강제한다.
&
는 익명 서브루틴을 요구한다. 이 익명 서브루틴이 첫번째 인자일 경우는 sub
키워드나, 뒤에 오는 컴마를 생략할 수 있다20
*
는 서브루틴이 bareword, 상수, 스칼라 식, 타입글로브, 타입글로브의 레퍼런스 등을 받아들일 수 있음을 의미한다.
use Symbol 'qualify_to_ref'; sub foo (*) { my $fh = qualify_to_ref(shift, caller); ... }
세미콜론(;)은 필수적인 인자와 선택적인 인자를 구분한다.
@
나 %
앞에는 쓸 필요가 없다.21
프로토타입의 가장 마지막 자리 또는 세미콜론 바로 앞자리에 $
가 들어갈 경우 그 대신에 _
를 쓸 수 있다
$_
가 대신 쓰이게 된다.
위 테이블에서 마지막 세 가지 예제가 구문해석기에 의해 어떻게 해석되는지 유의하라:
mygrep()
은 순수하게 리스트 연산자로만 해석된다.
myrand()
는 rand()
와 같은 단항 우선순위를 갖는 단항연산자로만 해석된다
mytime()
은 time()
처럼 순수하게 인자가 없는 연산자로 해석된다
mytime() + 2
를 얻게 된다. 프로토타입이 없었다면 mytime(2)
로 해석되었을 것이다:
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 /:/ );
scalar
가 붙게 된다.
@foo
가 전달되지 않고, @foo의 원소의 갯수인 1
이 전달된다.
split
이 스칼라 문맥에서 호출되게 되어, @_
인자리스트를 엉망으로 만들 것이다
프로토타입이 ()
인 함수들은 인라인함수가 될 가능성이 있다.
&
를 붙이지 않은 형태의 함수호출 자리에 치환되어 사용된다.
&
를 붙인 형태의 호출은 인라인이 되지 않는다
다음 함수들은 모두 인라인될 것이다:
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 $]; }
많은 내장 함수들을 오버라이드할 수 있다.
오버라이딩은 컴파일 타임에 함수를 모듈로부터 임포트하는 형태로 이뤄진다 -- 단지 호출 전 미리 선언하는 것만으로는 충분하지 못하다.
use subs
프라그마를 쓰면 import 문법을 사용하여 서브루틴을 미리 선언하고 내장 함수를 오버라이드할 수 있다:
use subs 'chdir', 'chroot', 'chmod', 'chown'; chdir $somewhere; sub chdir { ... }
기존 내장함수를 참조하려는 의도를 명백하게 나타내려면, 내장 함수의 이름 앞에 특수한 패키지 한정자인 CORE::
를 붙인다.
&open()
서브루틴이 있더라도, CORE::open()
은 언제나 내장 함수 open()
을 가리킨다.
\&CORE::open
과 같은 식으로 레퍼런스를 얻을 수 없다.
일반적으로 라이브러리 모듈들은 open
이나 chdir
과 같은 내장 함수의 이름을 기본 @EXPORT
리스트에 포함해서 익스포트하지 말아야 한다.
@EXPORT_OK
목록에 넣어서 사용자가 그 이름을 명시적으로 임포트하여 쓸 수 있게 한다.
open
을 불러와서 오버라이드할 수 있다:
use Module 'open';
use Module;
위의 메커니즘은 임포트를 요청한 패키지 내에서만 오버라이드가 이뤄진다. 만일 네임스페이스의 경계를 넘어서 모든 곳에서 내장함수를 오버라이드하고자 하길 원한다면, 서브루틴을 특별한 네임스페이스인 CORE::GLOBAL::
안으로 임포트한다.
glob
연산자를 오버라이드하여 정규표현식을 인식할 수 있도록 한 예:
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\$>; # 이름이 소문자로만 된 프라그마 모듈들을 출력
glob
을 전역적으로 오버라이드할 경우 모든 네임스페이스에서 glob의 동작이 변경되게 되며, 그 네임스페이스를 담당하는 모듈 쪽에서 그 사실을 인지하지 못 할 수 있다. 따라서 이런 오버라이드는 꼭 필요한 경우에만 극도로 주의를 기울여서 행하라.
REGlob
은 perl의 glob
을 오버라이드하기 위해 필요한 모든 기능을 제공하고 있지는 않다. 내장 glob은 스칼라 또는 리스트 문맥에서 호출되었을 때 서로 다르게 동작한다. 사실 많은 perl 내장 함수들이 이렇게 문맥에 따라 다르게 동작하며, 오버라이드할 경우 이런 동작을 지원해주어야 한다. glob
을 오버라이드하며 모든 기능을 지원하는 예는 표준 라이브러리에 있는 File::DosGlob
을 살펴보라.
내장 함수를 오버라이드할 경우, 대체되는 함수도 가능하면 내장 함수의 문법을 그대로 쓸 수 있게 작성해야 한다.
prototype
함수에 "CORE::내장함수이름"
인자를 넣으면 된다. (perlfunc의 "prototype" 참조)
어떤 내장 함수(system
이나 chomp
등)는 사용 문법을 프로토타입으로 표현할 수 없다. 이런 함수를 오버라이드할 경우는 그 함수의 원래의 문법을 완벽하게 모방할 수 없을 것이다.
내장 do
, require
, glob
은 오버라이드 할 수는 있으나, 그 함수들의 특수한 기능들 때문에, 함수의 문법이 보존된다. 따라서 오버라이드하는 대체함수를 위한 프로토타입을 정의할 필요가 없다. (다만 do BLOCK
문법을 오버라이드하는 것을 불가능하다)
require
는 이 외에도 특별한 기능이 있다: require Foo::Bar
의 형태로 오버라이드한 함수를 호출하면, 그 함수에 @_에 담겨서 실제로 전달되는 인자는 "Foo/Bar.pm"
이다. perlfunc의 "require"를 참조하라.
위에서 보았듯이 glob
을 오버라이드하면 글롭 연산자 <*>
도 같이 오버라이드된다.
비슷하게, readline
함수를 오버라이드하면 이와 동등한 I/O연산자 <파일핸들>
도 오버라이드된다. 또한 readpipe
을 오버라이드하면 ``
와 qw//
연산자도 오버라이드된다.
끝으로, 일부 내장함수(exists
또는 grep
등)은 오버라이드할 수 없다.
정의되지 않은 서브루틴을 호출할 경우 곧바로 치명적 에러가 발생하며 종료된다. (메쏘드로 사용되는 서브루틴의 경우 클래스 패키지와 그 상위클래스 어디에도 그 메쏘드가 없는 경우도 마찬가지)
AUTOLOAD
서브루틴이 그 패키지 또는 원래 서브루틴을 검색하는 데 사용되는 패키지 중에 정의되어 있을 경우, 이 서브루틴이 호출되며 원래의 서브루틴에 넘어갈 인자가 이 서브루틴으로 넘어간다.
AUTOLOAD
루틴이 있는 패키지에 속한 $AUTOLOAD
전역 변수에 담긴다.
import
또는 unimport
메쏘드를 호출하는 것은 그냥 무시된다.
많은 경우 AUTOLOAD
루틴은 eval()을 사용하여 원래 요청된 서브루틴의 정의를 로드한 후, AUTOLOAD의 스택프레임을 삭제할 수 있는 특별한 형태의 goto()를 사용하여 그 서브루틴을 실행한다. (예를 들어 AutoLoader 에 문서화되어 있는 표준 모듈의 소스를 참조하라) 하지만 단순히 원래의 서브루틴의 흉내만 내고 전혀 정의하지 않을 수도 있다.
system
을 호출하여 실행하도록 하는 예:
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';
더 복잡한 예는 표준 Shell에서 볼 수 있다, 여기서는 존재하지 않는 서브루틴 호출은 외부 프로그램을 호출하는 것으로 취급된다.
이런 자동 로드 메커니즘은 모듈 작성자가 자신의 모듈을 자동으로 로드할 수 있는 파일들로 분리하는 것을 돕기 위해 존재한다. AutoLoader, AutoSplit, SelfLoader, perlxs 등을 참조하라.
이 섹션의 내용은 주인장도 잘 모르는 부분이라, 번역에 오류가 있을 가능성이 높습니다
서브루틴의 선언이나 정의에는 그 서브루틴과 연관된 속성을 나열해 줄 수 있다.
use attributes
프라그마를 사용한 것처럼 목록을 콜론 또는 공백을 구분자로 해서 각각을 나눈 후에 처리된다.
use attrs
와는 달리, sub : 속성리스트
형태의 문법은 꼭 서브루틴 정의 안에 있어야 되는 게 아니라 미리 선언하는 곳에 있어도 된다.
각 속성의 명칭들은 간단한 식별자로서 사용할 수 있는 형태여야 한다 ('_' 문자를 제외하고는 구두점을 쓸 수 없다). 각 속성에는 파라메터 리스트가 뒤에 붙을 수 있으며, 괄호가 정확하게 중첩되었는지만 체크된다.
올바른 문법의 예: (실제 이런 이름의 속성 자체는 존재하지 않지만)
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';
속성 목록의 더 자세한 내용과 사용법에 대해서는 attributes와 Attribute::Handlers 참조
sub setup; # 이름만 선언 # 또는 use subs qw(setup get_input 등등); setup; # 사용 sub setup { ... } # 실제 정의
동일한 이름의 서브루틴이 둘 이상 있으면, 나중의 것이 적용된다.26
Perl의 built-in function 과 동일한 이름의 서브루틴을 호출하는 경우는 반드시 "&"가 필요하다. 이런 이유로, Learning Perl에서는 "Perl의 모든 내장 함수의 이름을 알고 있지 않다면" 항상 "&"를 붙일 것을 권장한다.27
그러나 최근 경향은 "&"를 앞에 붙이지 않는 것이다.28
주인장이 네이버 까페에 쓴 [다양한 상황에서 컨텍스트 확인]도 참고할 것:
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 )
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; }
my $a = 5; increment(\$a);프로토타입에 "\"+"변수타입심볼 형태로 적어주면 자동으로 레퍼런스를 취한다
sub increment(\$); my $a = 5; increment($a); sub increment(\$) { my $reference = shift; $$reference++; }
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"; ... }
sub something { print "Wibble!\n" } my $ref = \&something; # 익명 서브루틴 my $ref = sub { print "Wibble!\n" };
호출
&{$ref}; &{$ref}(@parameters); &$ref(@parameters); # 아래의 형태를 권장 $ref->(); $ref->(@parameters);
스코프를 벗어난 렉시컬 변수를 참조하는 서브루틴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, '.');
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"; }
{ # 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 } }
map, grep, 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; }
foo(int i, char c)
에서 i와 c같은 것들$패키지이름::변수이름
의 형태, 예: $main::str
perl -e "스크립트"
의 -e 대신 -E 를 사용하라는 이야기$/
while (<STDIN>) { }
형태의 경우 일단 $_가 자동으로 로컬라이즈되지 않는다. 따라서 그런 곳에서 명시적으로 while (local $_ = <STDIN>)
처럼 로컬라이즈를 해주는 것이 좋다는 뜻인 것 같기도 한데, 그럴 거면 차라리 my $_ = ...
처럼 my를 쓰는 게 나을 것이다. 아니면 이런 루프 안에서 다시 $_를 변경하는 구문을 넣을 경우 local을 써 주는 게 필수적이라는 뜻 같기도 한데, 어쨌거나 주인장은 정확한 해석을 못 하겠음grep { 코드 } 리스트
에서 { 코드 }
가 이렇게 익명 서브루틴을 넘기는 것이다$$$
면 반드시 스칼라 3개를 인자로 받아야 하고 $$;$
면 2개 또는 3개를 받을 수 있다. 하지만 프로토타입이 $$@
인 경우는 스칼라 2개만 인자로 넘겨받아도 에러가 나지 않고 마지막 @는 빈 리스트로 간주된다. 따라서 굳이 $$;@
라고 쓸 필요가 없다use warnings
프라그마를 사용하지 않아도 경고가 발생함