12 번째 수정본 소스 보기 : Perl/레퍼런스
마지막으로 [b]
-- Loading page list... --
내용출력
로그인[l]
Diary
[f]
최근변경내역
[r]
페이지목록[i]
횡설수설[2]
게시판[3]
링크
수정할 수 없습니다: Perl/레퍼런스 는 읽기 전용 페이지입니다.
== # perlref == 시간 날 때 perlref 문서를 요약해보려고 맘만 먹고, 시간이 안 나서 못 하고 있음; === # NAME === === # NOTE === === # DESCRIPTION === ==== # Making References ==== ==== # Using References ==== ==== # Symbolic references ==== ==== # Not-so-symbolic references ==== ==== # Pseudo-hashes: Using an array as a hash ==== ==== # Function Templates ==== === # WARNING === === # SEE ALSO === == # 기타 == === # autovivification === * http://www.effectiveperlprogramming.com/blog/1247 * http://www.effectiveperlprogramming.com/blog/1256 === # autovivification 과 서브루틴 파라메터 === 아직 값이 정의되지 않은 변수를, 해시 또는 배열의 레퍼런스인 것처럼 원소의 값을 읽거나 쓰려고 시도하면 자동으로 그 변수에 익명 해시 또는 익명 레퍼런스를 생성하여 할당해준다. 이것을 autovivification이라고 한다. {{{#!vim perl use Data::Dumper; my $ref1; # undef $ref1->{key} = "value"; # 해시 레퍼런스처럼 사용하여 원소 추가 print Dumper($ref1), "\n"; my $ref2; # undef my $val2 = $ref2->{key}; # 해시 레퍼런스처럼 사용하여 원소 읽기 시도 print Dumper($ref2), "\n"; my $ref3; # undef $ref3->[3] = 30; # 배열 레퍼런스처럼 사용하여 원소 추가 print Dumper($ref3), "\n"; my $ref4; # undef my $val4 = $ref4->[3]; # 배열 레퍼런스처럼 사용하여 원소 읽기 시도 print Dumper($ref4), "\n"; }}} {{{#!vim $VAR1 = { 'key' => 'value' }; $VAR1 = {}; $VAR1 = [ undef, undef, undef, 30 ]; $VAR1 = []; }}} * 실행결과에서 알 수 있듯이, 원소를 추가할 경우는 익명 해시 또는 익명 배열에 해당 원소까지 추가가 되고, 배열의 경우는 해당 원소보다 앞쪽 인덱스의 원소들은 undef으로 채워진다. * 반면에 원소를 읽으려고 시도했을 경우는 익명 해시 또는 익명 배열이 생성되지만 원소가 추가되지는 않는다. \\ \\ \\ 만일 값이 정의되지 않은 변수를, 서브루틴의 파라메터로 전달하고 그 서브루틴 안에서 해시 레퍼런스 또는 배열 레퍼런스처럼 사용하려고 하면 주의해야 한다. {{{#!vim perl sub copy_param { my $ref = shift; # 인자값이 복사된다 # 또는 아래처럼 해도 마찬가지 # my ( $ref ) = @_; $ref->{orange} = 5; } my $ref1; # undef $ref1->{apple} = 3; # autovivification 이 먼저 이뤄진 후 copy_param($ref1); # 서브루틴에 인자로 넘겨주고 print "ref1:", Dumper($ref1), "\n"; # 확인 my $ref2; # undef copy_param($ref2); # 서브루틴에 넘겨주고 print "ref2:", Dumper($ref2), "\n"; # 확인 }}} {{{#!vim ref1:$VAR1 = { 'apple' => 3, 'orange' => 5 }; ref2:$VAR1 = undef; }}} * 일단 익명 해시 레퍼런스를 담은 이후에 서브루틴에 인자로 넘어가면 서브루틴 내에서 원소를 추가하면 그 익명 해시에 원소가 추가된다. ** 이 경우 $ref1의 값을 복사해서 $ref에 담고, 이 때 $ref1과 $ref는 동일한 익명 해시를 가리키는 레퍼런스이다. * 그러나 아직 undef 상태에서 서브루틴에 인자로 넘어갈 경우는 서브루틴이 종료되어도 여전히 undef 그대로이다. ** 정확히는 서브루틴 내부에서는 $ref 변수에 autovivification 이 일어나지만, 이것이 $ref2에 영향을 주지 못한다. 따라서, 원하는 대로 동작하게 하려면 서브루틴 내에서 파라메터를 복사하지 말고, 파라메터의 alias인 @_의 원소들을 직접 접근해야 한다. {{{#!vim perl sub alias_param { $_[0]->{orange} = 5; # $_[0]은 인자와 alias 관계 } my $ref1; # undef $ref1->{apple} = 3; # autovivification 이 이뤄진 후 alias_param($ref1); # 서브루틴에 인자로 넘겨주고 print "ref1:", Dumper($ref1), "\n"; # 확인 my $ref2; # undef alias_param($ref2); # 서브루틴에 넘겨주고 print "ref2:", Dumper($ref2), "\n"; # 확인 }}} {{{#!vim ref1:$VAR1 = { 'apple' => 3, 'orange' => 5 }; ref2:$VAR1 = { 'orange' => 5 }; }}} * 두번째 경우도 제대로 autovivification 이 된 걸 알 수 있다. === # 객체 메쏘드의 레퍼런스 === 일반적인 서브루틴에 대한 레퍼런스는 쉽게 만들고 사용할 수 있다: {{{#!vim perl my $ref = \&func; $ref->('param'); }}} 그런데, [[/OOP]]의 객체의 메쏘드에 대한 레퍼런스는 어떻게 만들 수 있을까? {{{#!vim perl my $p1 = Person->new(); # 여기에 뭘 적어줘야...? my $ref = \&$p1->func; # 실패 my $ref = \$p1->func; # 실패 # 다음과 같이 호출할 수 있을까? $ref->('param'); }}} 위 두 가지는 다 실패한다.
$p1->func
가 먼저 실행되고 그 리턴값을 코드 레퍼런스로 다루려고 하거나(첫번째), 리턴값의 레퍼런스를 받아서 그 레퍼런스를 사용하여 호출을 하려(두번째) 하기 때문이다. 구글링해보니 Perl cookbook 에 내용이 있었다: * [http://docstore.mik.ua/orelly/perl/cookbook/ch11_09.htm Recipe 11.8. Creating References to Methods] * [http://www.perlmonks.org/?node_id=62737 How do I reference methods?] - Perl Monks 위 두 곳의 내용과 [[주인장]]이 이래저래 테스트해 본 결과를 정리하면 다음과 같다. Perl Cookbook에서 최선의 방안으로 제시하는 방법은 [[/Closure]]를 사용하는 것이다: {{{#!vim perl $mref = sub { $p1->func(@_) }; $mref->('param'); }}} 이 경우 $p1이 변수 스코프에서 벗어난 상태라도 여전히 호출 가능하다: {{{#!vim perl my $mref; { my $p1 = Person->new(); $mref = sub { $p1->func(@_) }; } $mref->('param'); }}} 유의할 점. 만약 $mref가 설정된 후에 $p1이 다른 객체를 가리키도록 변경되었다면, $mref는 새로 바뀐 객체에 대해 메쏘드를 호출하게 된다. 그 외에 몇 가지 방법은 (위 링크들에 언급되었거나 내가 시도해보거나 등) 일반적인 레퍼런스처럼 가져와서 사용: {{{#!vim perl $mref = \&Person::func; # 일반적인 서브루틴 레퍼런스와 결국 같다 $mref->($p1,'param'); # 그래서 첫번째 인자로 객체 자신을 넣어줘야 한다 }}} UNIVERSAL 클래스에서 제공하는
can
메쏘드([[/OOP]]참조)를 써도 비슷하다: {{{#!vim perl $mref = $p1->can('func'); $mref->($p1,'param'); }}} can 메쏘드를 이용하려면 반드시 객체가 있어야 하니까 패키지 이름을 사용한 위의 방법보다 안 좋아 보이긴 한다. 게다가 두 방법 다 어차피 객체 자체를 첫번째 인자로 줘야 하기 때문에,
$p1->can(...)
와 같이 레퍼런스를 얻은 후에 호출은
$mref->($p2,...)
처럼 다른 객체에 대해 쓸 수도 있다. 만일 메쏘드 이름을 스트링이 아니라 동적으로 결정하려면: {{{#!vim perl my $name = 'func'; $mref = $p1->can($name); # can()의 인자로 변수를 넣는 건 특별할 게 없다. $mref = \&{"Person::$name"}; # 이건 좀 기괴해 보인다. }}} 위의 마지막 줄의 방법은, strict 프라그마, 특히
strict refs
가 적용된 상태에서도 쓸 수 있다. (Perldoc:strict 참고) 내가 테스트해본 바로는 슈퍼클래스의 메쏘드에 대해서도 다 잘 동작한다. 물론 패키지 이름을 적어준 방법의 경우는 해당 슈퍼클래스의 패키지 이름을 찾아서 적어줘야 하므로 사용하기 매우 까다로와지겠다. 그리고 위의 Perl Monks 링크에 가 보면 이외에 여러 가지 아이디어가 나와 있다. 나도 다 읽어보진 않았음. == # 기타 == * [http://ko.perlmaven.com/symbolic-reference-in-perl Perl의 심볼릭 레퍼런스] == # Comments ==
----
---- [[컴퓨터분류]]
Perl/레퍼런스
페이지로 돌아가기 |
다른 수정본 보기