-
- 1. perl 의 변수
-
- 2. 특수한 스칼라 변수
-
- 3. Scope
-
-
- 3.1. global (package) variable
-
- 3.2. lexical variable
-
- 3.3. runtime scope
-
- 3.4. 스코프에 영향을 미치는 것
-
4. Global 변수 보충
-
- 5. 수와 문자열
-
-
- 5.1. 수로 다룰 때와 문자열로 다룰 때의 차이로 인한 오작동 사례
-
6. 기타 & Comments
-
1. perl 의 변수
- scalar
- array
- @배열변수명 = (값1, 값2, ... )
- $배열변수명[인덱스] = 값
- 각 element 는 $array[2] 와 같이 사용
- $#배열변수명은 배열의 마지막 인덱스, @배열변수명은 원소의 갯수를 나타낸다. (값이 1만큼 차이나니 주의)
- print "@array" 는 원소들을 공백으로 구분해서 출력, print @array 는 구분하지 않고 이어서 출력
- associative array (hash)
- %해쉬변수명 = ( 키1, 값1, 키2, 값2, ... )
- $해쉬변수명{키} = 값
2. 특수한 스칼라 변수
- $" - 구획 구분자. print "@array";는 print join($", @array) 한 것과 같다.
- $$ - 프로세스ID
- $/ - 줄 구분자. `명령어` 연산자가 리스트 구문에서 반환될 때는 출력의 각 라인을 원소로 갖는 리스트를 반환하는데, 이 때 줄을 구분하는 용도로 쓰인다. <파일핸들>을 통해서 변수를 읽을 때도 사용
- $. - <>를 사용하여 읽고 있는 파일의 현재 행
- $? - 명령어 수행의 결과. 자식 프로세스의 반환값
- $! - 시스템 에러 메시지 (예: open FH, "log.txt" or die "$!";)
3. Scope
3.1. global (package) variable
- 그냥 사용하거나, use strict; 에서는 our $var; 또는 use vars qw($var); 등으로 선언
$main::name = "Your Name Here";
$Fred::name = "Fred Flintstone";
$Barney::name = "Barney Rubble";
print "\$name in package main is $name\n";
print "\$name in package Fred is $Fred::name\n";
print "\$name in package Barney is $Barney::name\n";
$main::name = "Your Name Here";
$Fred::name = "Fred Flintstone";
$Barney::name = "Barney Rubble";
print "\$name in package main is $name\n";
package Fred;
print "\$name in package Fred is $name\n";
package Barney;
print "\$name in package Barney is $name\n";
package main;
- 패키지 이름 부분만 생략하여 "$::foo" 등과 같이 쓸 경우는 main 패키지로 간주한다. ("$main::foo")
3.2. lexical variable
my $var; 로 선언
3.3. runtime scope
local $var; - 해당 블록이 끝날때까지는 서브루틴으로 점프해도 영향을 미친다. lexical 변수는 그렇지 않음
my $x = 10;
$_ = "alpha";
{
my $x = 20;
local $_ = "beta";
somesub();
}
somesub();
sub somesub {
print "\$x is $x\n";
print "\$_ is $_\n";
}
$x is 10
$_ is beta
$x is 10
$_ is alpha
$/와 같은 특수 변수를 다루는 경우를 제외하면, local을 쓰는 것을 지양하고 my를 쓸 것. 전역 변수를 조작해야 하는 경우가 생긴다면 디자인을 새로 하는 것을 고려해보라고 함
3.4. 스코프에 영향을 미치는 것
요점:
- 렉시컬 변수는 그 변수의 스코프 내에서만 접근할 수 있다.
- 블록(중괄호를 사용하여 만드는)을 사용하여 스코프를 정의할 수 있다
- 파일도 그 자체로 스코프를 정의한다.
- 패키지는 스코프를 정의하지 않는다 (주의)
4. Global 변수 보충
요점:
- 패키지 변수는 전체 프로그램 내에서 볼 수 있다.
- 패키지 변수는 그 변수가 속한 패키지를 안다면 프로그램 내 어디에서든 접근할 수 있다. 이 변수들은 package global이라 한다.
- 해당 패키지 안에서는 변수명만 써도 되고, 다른 패키지에서 접근할 때는
$패키지이름::변수이름
으로 사용
- 현재 패키지가 무엇이든 간에, $_나 $@ 등 perlvar 문서에 언급되어 있는 Perl의 특수한 변수들은 main:: 패키지 내에 정의된다. 이 변수들은 true global이라 한다.
- $a와 $b는 특수한 변수이나, 현재 패키지 내에 정의된다. 이 변수들은 special package global이라 한다.
- 출력 파일핸들에 관계된 특수 변수들은 파일핸들마다 각각 유지되며, 그 변수들의 현재값은 현재 디폴트 파일핸들로 설정된 파일핸들에 연관된 값이다.
- 디폴트 파일핸들은 select 함수를 사용하여 지정할 수 있다.
5. 수와 문자열
- 스칼라 변수는 스트링으로 사용될 때와 수로 사용될 때의 값이 각각 별개로 저장되며, Perl/Scalar-Util 모듈의
dualvar
함수를 사용하여 그 둘을 서로 별개의 값으로 지정할 수 있다.
5.1. 수로 다룰 때와 문자열로 다룰 때의 차이로 인한 오작동 사례
어떤 스칼라 변수의 초기값이 숫자였을 때, 이것을 문자열 결합 연산자 .
를 쓰거나 따옴표 안에 넣어 interpolate시키거나 할 경우 그 시점에 문자열 값이 추가되는 걸 알 수 있다.
use Devel::Peek;
my $inc = 5;
Dump($inc);
print '<',$inc,'>', "\n";
Dump($inc);
print '<'.$inc.'>', "\n";
Dump($inc);
SV = IV(0x7fa88382d728) at 0x7fa88382d738
REFCNT = 1
FLAGS = (PADMY,IOK,pIOK)
IV = 5
<5>
SV = IV(0x7fa88382d728) at 0x7fa88382d738
REFCNT = 1
FLAGS = (PADMY,IOK,pIOK)
IV = 5
<5>
SV = PVIV(0x7fa88382f820) at 0x7fa88382d738
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 5
PV = 0x7fa88340bd70 "5"\0
CUR = 1
LEN = 16
그리고 Search::Elasticsearch 의 메쏘드 중에, 간단한 자바(정확히는 자바 기반인 MVEL) 코드를 바로 넣어서 elasticsearch 엔진에 전달할 수 있는데,
$es->update(
...
body => {
script => "ctx._source.num += inc",
params => { inc => $inc },
}
);
$inc가 수 값만 있을 때는 JSON으로 변환된 내용이 "params" : { "inc" : 5 }
인데, PV값도 있을 경우는 "params" : { "inc" : "5" }
로 전달되고, 이게 위의 += inc
에 적용되면 수의 덧셈이 아니라 문자열 합치기가 되어버린다. 기존의 num 필드의 값이 3이었다면 8이 되어야 할 것이 "35"가 되어버리는 불상사가 생김.
앞에서 봤듯이 스칼라 변수에 PV값이 생기는 시점이 문자열처럼 다루기 시작할 때라서, 처음 만들었던 코드는 아무 문제가 없었는데, 비슷하게 만든 다른 코드는 계속 문제가 생기고, 알고보니 테스트를 위해 미리 변수값을 print 시키면서 "[$inc]"
이런 식으로 스트링으로 다룬 게 원인이었다. Perl 스칼라 변수의 특성과 특정 모듈의 동작과 자바의 +
연산자 오버로딩이 만나서 아주 기묘한 결과를 낳음.
해결책은 params => { inc => 0+$inc }
와 같이, $inc 변수의 값을 다시 온전한 숫자로 변환하는 것.
6. 기타 & Comments
- 변수의 스코프에 관한 글. 단 90년대 후반에 작성된 글이라서, 최신 버전의 Perl에는 좀 안 맞는 내용도 있다. (예를 들어 파일핸들을 이제는 그냥 my $fh 같이 렉시컬 스칼라변수를 쓰면 된다)
- 이 페이지에 들어갈 얘기는 아니지 싶지만 일단... 스트링 내의 변수치환과 다른 이스케이프 시퀀스들의 적용 순서에 관한 얘기. 스트링과 정규식에서 좀 다르다. (왜 다르게 했을까-_-? 어쩐지 옛날부터 뭔가 자꾸 예상과 빗나간다 싶었어)
컴퓨터분류