-
Notifications
You must be signed in to change notification settings - Fork 15
scope codeblock my, local, our
aero edited this page Sep 19, 2012
·
1 revision
Association(관련) = Binding(연결)
$fruit = "apple";
Code block
모든 변수가 프로그램의 생명주기 전체에 걸쳐서 필요하지는 않다는 사실에 association의 scope를 제한하자는 생각에서 나왔음
Scope
association의 Scope는 해당 영역 내에서 어떤 association을 볼 수 있는가를 말함.
association은 어떤 변수가 존재하는 scope를 벗어날 때 사라짐.
global variable(전역변수)는 실행되기전 컴파일될때 만들어지고 프로그램이 끝날때 사라지며 임으로 파괴시킬 수 없으며 symbol table로 부터 결정되어 진다.
scope는 environments란 말로 정의할 수 있으며 environments는 code block { }으로 만들어진다.
environments는 code block안에서는 그 안 제일 바깥쪽에서는 파일전체가 된다.
# Environment A
{
# Environment B
{
# Environment C
}
# Environment B resumes
{
# Environment D
}
# Environment B resumes
}
# Environment A resumes
만약 현 environment내에 사용하는 변수가 정의 되어 있지 않으면 자동으로 한 단계씩 올라가며 찾아서 사용한다. 만약 최상위(파일전체 - Environment A)까지 올라가서도 없으면 에러가 발생할 것이다.
Package
Perl은 프로그램이 여러부분으로 나뉠때 변수들이 충돌하지 않도록 서로 다른 namespace를 가질 수 있는 회피수단을 제공한다. 그것이 package이다.
package를 정의하지 않으면 기본적으로 main 패키지이다.
package의 선언
package package_name;
package의 범위
package는 code block이 끝나거나 eval()의 괄호가 닫힐 때 또는 파일의 끝을 만날때 중 아무 것이나 먼저 만나는 곳에서 그 범위가 끝난다.( my, local과 같은 scope를 가진다.)
#!/usr/bin/perl
# main package
{
package Apple;
# package Apple extends to the end of the block
$var = 3;
}
# main package
여기서 주의해야 할점은 package는 namespace충돌을 방지하기 위한것이지 scope와는 상관이 없다는 것이다. 위 예제에서 package Apple의 범위가 끝나는 code block이 닫혀도 여전히 $Apple::var는 값을 가지며 접근가능하다.
Package variable 참조
scalar 변수라면 $패키지이름::변수명 으로 참조한다.
$main::var
$::var 생략하면 main package를 가르킴
$var - 같은 package 내에서는 그냥 변수명만 사용가능
$Apple::var - 명시적으로 package명을 지정
Package Variable과 Symbol table
각 package는 각자의 symbol table을 유지한다. non-lexical variable(my에 의해 선언되지 않은변수)는 모두 symbol table에 저장된다.
예로 @INC 배열은 Perl 라이브러리 디렉토리 리스트를 가지고 있다.따라서 symbol table는 일종의 유사 해쉬 구조를 가지므로 %main:: 안에 INC라는 key가 있고 $main::{INC}는 INC라는 이름의 symbol안의 변수들에 대한 reference 리스트를 가지는 typeglob을 가르킨다. 이것은 glob 연산자 *를 써서 *main::INC 로 나타낼 수 있는데 이것이 typeglob의 형태이다.
main 패키지에 $c라는 scalar 변수가 선언되어있다면 symbol table은 다음과 같은 유사 해쉬 구조를 가지며 *main::c는 c라는 이름의 symbol이 가지는 각종 변수형태에 대한 reference를 가지고 있다.
# package Identifier Type Variable
# main:: ---> c -+- SCALAR - $c
# +- ARRAY - @c
# +- HASH - %c
# +- CODE - &c
# +- IO - 파일 혹은 디렉토리 핸들
# +- GLOB - *c
# +- FORMAT - 포맷 이름
# +- NAME
# +- PACKAGE
$c값을 가져오는 방법은
${*main::c{SCALAR}} - 실제로 이렇게는 쓰지 않는다. 내부 동작을 보여주기 위함 *main::c{SCALAR}는 c이름을 가지는 $c 변수에 대한 reference이고 이것을 scalar dereference ${}를 적용하면 해당 scalar값 $c가 되는것이다. ( ${ }로 스칼라로 dereference 한다고 지정했므로 ${*main::c} 처럼 {SCALAR} 는 생략가능 )
$main::c
$c - main(기본 package) package 내부이면
@c 일경우
@{*main::c{ARRAY}} - ( @{ }로 배열로 dereference 한다고 지정했으므로 @{*main::c} 처럼 {ARRAY} 생략가능 )
@main::c
@c
typeglob는 Perl에 reference가 없었을때 reference를 구현하기 위한 용도로도 쓰였음.
perl -e '$c=3; $d=*c;$c=2;print $$d;'
2
perl -e '$c=3; $d=\$c;$c=2;print $$d;'
2
symbol table 덤프해보기
perl -e 'print map{$_," -> ",$main::{$_},"\n"} keys %main::;'
...
stdin -> *main::stdin
ARGV -> *main::ARGV
INC -> *main::INC
ENV -> *main::ENV
abc -> *main::abc
...
Lexical scoping, Dynamic scoping
code block같은 environments에 의해 결정된다.
Lexical scoping
lexically scoped된 association은 그것이 정의된 순간부터 해당 environment 안쪽에 동일한 lexically scoped variable이 올때까지 볼 수 있다. Perl에서 lexically scoped variable은 my로 선언한다. my로 선언된 변수는 해당 lexical scope내에서 symbol table에 기록되는것이 아니라 scratchpad라는 메모리 영역에 보관되고 영역을 벗어나면 garbage collection된다. 따라서 scope외부에서는 보지 못한다.
my $a = "Hello ";
{
$a .= "World\n";
print $a; # Hello World
my $a = "Bye!\n";
print $a; # Bye!
}
print $a; # Hello World
lexical scoping model은 포함된 code block안에서 독립적으로 결정되고 컴파일 단계에 이미 고정적이기 때문에 dynamic scoping 모델보다 처리속도가 빨라서 사용이 권장된다. ( package 내부에서도 외부에서 참조할 필요가 없는 변수의 경우 굳이 our로 사용할 필요없이 파일전체 environment 하에서 my로 선언해서 사용해도 무방하며 그렇게 하는 것이 좋다. )
Dynamic scoping
Dynamic scoping은 environment 내의 scratchpad대신에 call stack이라는 것에 기반하여 작동한다. Perl에서 이런 변수들은 local 로 선언한다. local 변수가 선언되면 symbol table내에 기존의 해당 이름의 global 변수가 hidden stack에 임시로 저장되고 선언된 scope가 끝날때 까지 local로 선언된 변수가 원래 값을 대체하여 해당 scope내에서는 global변수처럼 작동하며(외부로 함수를 호출해도 그 상황에서는 global variable 처럼 동작하므로 외부에서도 볼 수 있다.) scope가 끝나면 hidden stack에 저장해 놓았던 원래 값이 복원된다.
sub greeting {
print $a; # Bye!
}
$a = "Hello ";
{
$a .= "World\n";
print $a; # Hello World
local $a = "Bye!\n";
greeting();
}
print $a; # Hello World
global variable을 선언하는 방법
use strict; pragma를 쓰지 않고 그냥 변수를 사용한다.(use strict; pragma를 사용하면 모든 변수에 대해 my,local,our, use vars를 사용해서 변수의 scope를 명시적으로 선언해야한다. 현대 Perl 프로그래밍에서는 모든 프로그램에 대해서 기본적으로 use strict; pragma 사용을 권장하므로 규모있는 Perl 프로그램에서 이런식으로 사용하는 일은 드물다. $a,$b 의 경우 use strict; pragma를 사용해도 에러가 발생하지 않고 그냥 선언이 되는데 이것은 Perl sort에 쓰는 $a,$b가 Perl 내부적 용도에 의해 기본으로 global variable로 사용되기 때문이다. 이것을 잘 모르고 버그로 보고하는 사례가 많다고 함.)
$main::var = 1 형식으로 패키지명을 명시적으로 지정하여 선언 - 이렇게 선언하면 use strict; pragma를 쓸 경우 접근할때도 명시적으로 패키지명을 지정해야 한다.
use vars qw/$var/; 형식으로 선언하고 사용
our를 사용한다.
일반적 global variable은 해당 변수가 선언된 package가 다른 package로 변경되었을 경우 명시적으로 패키지이름을 붙여서 접근해야 하나 our로 선언한 변수는 해당 package에만 국한 되지 않고 our가 선언된 lexical scope내에서 package이름을 사용하지 않고 접근가능하다.
#!/usr/bin/perl
use strict;
use warnings;
$main::var = 1;
print $main::var,"\n";
package test;
use vars qw/$var3/; #usr vars를 사용 global variable를 선언
my $var1 = 1;
our $var2 = 2;
$var3 = 3; # use vars로 이미 global variable로 선언해놓았음
{
our $var4 = 4;
}
{
package test2;
our $var5 = 5;
}
#print $var5,"\n"; # error our $var5의 scope가 끝났으므로
# 하지만 여전히 $test2::var5로는 접근이 가능
package test3;
print $var1,"\n"; #my $var1의 scope는 파일전체 이므로
# package변경과 관련없이 그대로 사용가능
print $var2,"\n"; # 위의 our의 차이점을 상기
# print $var3,"\n"; # error 패키지가 바뀌었으므로 바로 접근불가.
print $test::var3,"\n"; # 명시적으로 패키지를 지정하면 접근가능
#print $var4,"\n"; # error our $var4의 scope는 끝났으므로 불가
print $test::var4,"\n"; #명시적으로 패키지를 지정하면 여전히 가능
#print $var5,"\n"; # error our $var5의 scope는 끝났으므로 불가
print $test2::var5,"\n";#명시적으로 패키지를 지정하면 여전히 가능