Perl 연산자
Perl 연산자 (perlop)
Perl에서 연산자는 피연산자(operand)의 타입과 무관하게 무슨 연산을 수행할지를 결정해요. 예를 들어 $x + $y는 항상 숫자 덧셈이고, $x나 $y에 숫자가 들어 있지 않으면 먼저 숫자로 변환하려 시도하죠. 이번 장에서는 연산자 우선순위·결합성부터 따옴표류 연산자, I/O 연산자까지 Perl 연산자의 전모를 훑어볼게요.
출처: perldoc - perlop
개요 (DESCRIPTION)
Perl에서 연산자는 첫 인자의 타입에 따라 연산이 결정되는 다른 많은 동적 언어들과 대조적이에요. 이것은 Perl이 어떤 연산자를 두 가지 버전으로 가진다는 뜻이기도 해요. 하나는 숫자 비교용, 하나는 문자열 비교용이죠. 예를 들어 $x == $y는 두 숫자가 같은지 비교하고, $x eq $y는 두 문자열을 비교해요.
다만 예외가 몇 가지 있어요: x 연산자는 왼쪽 피연산자의 타입에 따라 문자열 반복이 될 수도, 리스트 반복이 될 수도 있고, &, |, ^, ~는 문자열 또는 숫자 비트 연산이 될 수 있어요(use v5.28 이상이거나 bitwise 기능이 효과를 발휘하면 문자열 조작용의 별도 버전이 각 연산자에 생기지만요).
연산자 우선순위와 결합성 (Operator Precedence and Associativity)
연산자 우선순위와 결합성은 대략 수학에서처럼 동작해요.
*연산자 우선순위(operator precedence)*는 어떤 연산자가 다른 것들보다 더 단단히 묶인다는 뜻이에요. 예를 들어 2 + 4 * 5에서 곱셈이 더 높은 우선순위라, 4 * 5가 덧셈의 오른쪽 피연산자로 묶여요. (2 + 4) * 5가 아니라 2 + (4 * 5)처럼요. 그래서 6 * 5 == 30이 아니라 2 + 20 == 22가 돼요.
*연산자 결합성(operator associativity)*은 같은 연산자들을 연달아 쓸 때 무슨 일이 일어나는지 정의해요. 보통 왼쪽이나 오른쪽으로 묶여요. 예를 들어 9 - 3 - 2에서 뺄셈은 왼쪽 결합이라, 9 - 3이 두 번째 뺄셈의 왼쪽 피연산자로 묶여요. (9 - 3) - 2로, 6 - 2 == 4가 돼요. 9 - (3 - 2)가 아니고요.
모든 피연산자를 평가한 뒤 값을 결합하는 단순한 연산자에서는 우선순위와 결합성(그리고 괄호)이 그 결합 연산들에 어느 정도 순서 요구사항을 암시해요. 하지만 연산 순서가 전적으로 결정되지는 않아요. 사실 Perl에는 피연산자가 왼쪽에서 오른쪽 순서로 평가된다는 일반 규칙이 있어요. &&= 같은 몇몇 연산자는 피연산자가 아예 평가되지 않을 수 있는 특별한 평가 규칙을 가져요.
일부 비교 연산자는 결합성으로서 같은 우선순위의 연산자들과 **연쇄(chaining)**돼요. 각 비교는 그것을 둘러싼 두 인자에 수행되고, 내부 인자는 두 비교에 참여하며, 비교 결과는 암묵적으로 AND됩니다. 그래서 "$x < $y <= $z"는 "$x < $y && $y <= $z"처럼 정확히 동작해요. ANDing은 &&처럼 단락(short-circuit)해서 하나가 거짓이 되는 즉시 비교 시퀀스를 멈춰요.
연쇄 비교에서 각 인자 표현식은 두 비교에 참여하더라도 최대 한 번 평가돼요. 단, 평가 결과는 각 비교마다 가져와져요. 이건 내부 인자의 계산이 비싸거나 비결정적일 때 중요해요. 예를 들어:
if ($x < expensive_sub() <= $z) { ...
는 서브루틴이 한 번만 호출된다는 점에서 다음에 더 가깝죠:
my $tmp = expensive_sub();
if ($x < $tmp && $tmp <= $z) { ...
일부 연산자는 대신 **비결합(non-associative)**인데, 같은 우선순위의 연산자 시퀀스를 쓰면 문법 오류가 된다는 뜻이에요. 예를 들어 "$x .. $y .. $z"는 오류예요.
Perl 연산자는 다음의 결합성과 우선순위를 가지며, 높은 것부터 낮은 것 순으로 나열돼요. C에서 차용한 연산자는 C의 우선순위 관계를 유지해요.
left terms and list operators (leftward)
left ->
nonassoc ++ --
right **
right ! ~ ~. \ and unary + and -
left =~ !~
left * / % x
left + - .
left << >>
nonassoc named unary operators
nonassoc isa
chained < > <= >= lt gt le ge
chain/na == != eq ne <=> cmp ~~
left & &.
left | |. ^ ^.
left &&
left || ^^ //
nonassoc .. ...
right ?:
right = += -= *= etc. goto last next redo dump
left , =>
nonassoc list operators (rightward)
right not
left and
left or xor
다음 섹션들이 이 연산자들을 자세히 다뤄요. 각 섹션은 단일 우선순위 수준의 모든 연산자를 다루고, 위 표처럼 높은 우선순위 순으로 정렬돼 있어요. 많은 연산자는 객체에 대해 오버로드할 수 있어요. overload 참고.
항(Terms)과 리스트 연산자 (Terms and List Operators (Leftward))
TERM은 Perl에서 가장 높은 우선순위를 가져요. 변수, 따옴표와 따옴표류 연산자, 괄호 안의 어떤 표현식, 그리고 인자를 괄호로 감싼 어떤 함수가 여기 포함돼요. 사실 이 의미에서 진짜 함수는 없고, 인자 주위에 괄호를 둬서 함수처럼 행동하는 리스트 연산자와 단항 연산자가 있을 뿐이에요. 이것들은 모두 perlfunc에 문서화돼 있어요.
어떤 리스트 연산자(print() 등)나 단항 연산자(chdir() 등) 바로 뒤에 다음 토큰으로 왼쪽 괄호가 오면, 그 연산자와 괄호 안 인자들은 일반 함수 호출처럼 가장 높은 우선순위로 취급돼요.
괄호가 없으면 print, sort, chmod 같은 목록 연산자의 우선순위는 연산자 왼쪽을 보느냐 오른쪽을 보느냐에 따라 매우 높거나 매우 낮아요. 예를 들어:
@ary = (1, 3, sort 4, 2);
print @ary; # prints 1324
sort 오른쪽의 쉼표들은 sort보다 먼저 평가되지만, 왼쪽의 쉼표들은 나중에 평가돼요. 다시 말해 리스트 연산자는 뒤따르는 모든 인자를 삼키고, 그다음 앞의 표현식에 대해 단순한 TERM처럼 동작하는 경향이 있어요. 괄호에 주의하세요:
# These evaluate exit before doing the print:
print($foo, exit); # Obviously not what you want.
print $foo, exit; # Nor is this.
# These do the print before evaluating exit:
(print $foo), exit; # This is what you want.
print($foo), exit; # Or this.
print ($foo), exit; # Or even this.
또한 다음이:
print ($foo & 255) + 1, "\n";
첫눈에 예상한 대로 동작하지 않을 거예요. 괄호가 print의 인자 목록을 감싸고 그것을 평가해요($foo & 255의 결과를 출력). 그런 다음 print의 반환값(보통 1)에 1을 더해요. 결과는 이런 식이 되죠:
1 + 1, "\n"; # Obviously not what you meant.
제대로 하려면 이렇게 써야 해요:
print(($foo & 255) + 1, "\n");
TERM으로 파싱되는 것에는 do {}와 eval {} 구조, 서브루틴·메서드 호출, 그리고 익명 생성자 []와 {}도 있어요.
화살표 연산자 (The Arrow Operator)
->는 C와 C++에서처럼 중위(infix) 역참조 연산자예요. 오른쪽이 [...], {...}, (...) 첨자 중 하나면, 왼쪽은 각각 배열·해시·서브루틴에 대한 하드 또는 심볼릭 참조여야 해요. 그 외의 경우 오른쪽은 메서드 이름이거나 메서드 이름이나 서브루틴 참조를 담은 단순 스칼라 변수이고, (메서드 이름이라면) 왼쪽은 객체(bless된 참조)나 클래스 이름(패키지 이름)이어야 해요. perlobj 참고.
오른쪽은 & 시길이 붙은 서브루틴 이름일 수도 있어요. 이건 어휘적 메서드 호출처럼 보이는 것을 만들며, 메서드 서브루틴이 객체 클래스의 패키지 안에서 이름으로 검색되는 대신 어휘적으로 해석돼요. 이 해석은 전적으로 컴파일 타임에 일어나고, 런타임엔 일반 서브루틴 호출과 동일하게 동작해요. 역참조(메서드 호출과 반대) 경우는 postderef 기능으로 다소 확장돼요.
자동 증가와 자동 감소 (Auto-increment and Auto-decrement)
++와 --는 C에서처럼 동작해요. 변수 앞에 놓으면 값을 반환하기 전에 1 증가·감소시키고, 뒤에 놓으면 값을 반환한 뒤에 증가·감소시켜요.
$i = 0; $j = 0;
print $i++; # prints 0
print ++$j; # prints 1
C에서처럼 Perl도 변수가 언제 증가·감소하는지는 정의하지 않아요. 값이 반환되기 전후 언젠가 일어난다는 것만 알죠. 이것은 같은 문장에서 변수를 두 번 수정하면 정의되지 않은 동작이 된다는 뜻이기도 해요. 이런 문장은 피하세요:
$i = $i ++;
print ++ $i + $i ++;
자동 증가 연산자에는 작은 마법이 조금 더 있어요. 숫자이거나 숫자 문맥에서 쓰인 적이 있는 변수를 증가시키면 정상 증가예요. 하지만 변수가 설정된 이후 문자열 문맥에서만 쓰였고, 빈 문자열이 아니며 패턴 /^[a-zA-Z]*[0-9]*\z/에 매치되는 값을 가지면, 문자열로 증가가 수행돼요. 각 문자는 자기 범위 안에서 캐리하며 보존돼요:
print ++($foo = "99"); # prints "100"
print ++($foo = "a0"); # prints "a1"
print ++($foo = "Az"); # prints "Ba"
print ++($foo = "zz"); # prints "aaa"
undef는 항상 숫자로 취급되고, 특히 증가 전에 0으로 바뀌어요(그래서 undef 값의 후위 증가는 undef가 아니라 0을 반환해요). 자동 감소 연산자는 마법적이지 않아요.
지수 (Exponentiation)
이진 **는 지수 연산자예요. 단항 마이너스보다 더 단단히 결합해서 -2**4는 (-2)**4가 아니라 -(2**4)예요. (C의 pow(3) 함수로 구현되며, 내부적으로 double로 동작해요.)
일부 지수 표현식은 제대로 정의되지 않아요: 0**0, 1**Inf, Inf**0이 그것이에요. 이 특수한 경우들에 특정 결과를 기대하지 마세요. 결과는 플랫폼에 따라 달라요.
심볼릭 단항 연산자 (Symbolic Unary Operators)
단항 !는 논리 부정, 즉 "not"을 수행해요. 더 낮은 우선순위 버전은 not을 참고하세요.
단항 -는 피연산자가 숫자(숫자처럼 생긴 문자열 포함)면 산술 부정을 수행해요. 피연산자가 식별자면 마이너스 부호와 식별자를 이어붙인 문자열이 반환돼요. 그 외에 문자열이 덧셈·뺄셈 부호로 시작하면 반대 부호로 시작하는 문자열이 반환돼요. 이 규칙의 한 효과는 -bareword가 문자열 "-bareword"와 동등하다는 거예요. 하지만 문자열이 비알파벳 문자로 시작하면 Perl은 숫자로 변환을 시도하고 산술 부정을 수행해요. 문자열이 깨끗하게 숫자로 변환되지 않으면 Perl은 Argument "the string" isn't numeric in negation (-) at ... 경고를 줘요.
단항 ~는 비트 부정(1의 보수)을 수행해요. 예를 들어 0666 & ~027은 0640이에요. 결과 폭은 플랫폼에 따라 달라요: ~0은 32비트 플랫폼에서 32비트, 64비트 플랫폼에서 64비트예요. 그래서 특정 비트 폭을 기대한다면 & 연산자로 초과 비트를 마스킹하는 걸 기억하세요. Perl 5.28부터 255를 넘는 서수 값을 가진 문자를 포함한 문자열을 보수하려 하면 치명적 오류예요.
bitwise 기능이 켜져 있으면(use feature 'bitwise' 또는 use v5.28) 단항 ~는 항상 인자를 숫자로 취급하고, 대체 형태 ~.는 항상 문자열로 취급해요. 그래서 32비트 플랫폼에서 ~0과 ~"0"은 둘 다 2**32-1을 주는 반면, ~.0과 ~."0"은 둘 다 "\xff"를 줘요.
단항 +는 문자열이어도 아무 효과가 없어요. 함수 이름을, 아니면 함수 인자의 전체 목록으로 해석될 괄호 표현식과 구분하는 데 문법적으로 유용해요.
단항 \는 참조를 만듭니다. 피연산자가 단일 시길(sigil) 붙은 것이면 그 객체에 대한 참조를 만들어요. 괄호로 감싼 목록이면 목록에 언급된 것들에 대한 참조를 만들죠. 그 외에는 피연산자를 리스트 문맥에 두고, 피연산자가 제공하는 목록의 스칼라들에 대한 참조 목록을 만들어요. perlreftut와 perlref 참고. 이 동작을 문자열 안의 백슬래시와 혼동하지 마세요. 두 형태 모두 다음 것을 보간으로부터 보호한다는 관념을 담고 있긴 하지만요.
바인딩 연산자 (Binding Operators)
이진 =~는 스칼라 표현식을 패턴 매치에 바인딩해요. 어떤 연산들은 기본적으로 $_ 문자열을 검색하거나 수정해요. 이 연산자는 그런 연산을 다른 문자열에 대해 동작하게 만들어요. 오른쪽 인자는 검색 패턴·치환·음역(transliteration)이고, 왼쪽 인자는 기본 $_ 대신 검색·치환·음역될 대상이에요. 스칼라 문맥에서 반환값은 대체로 연산의 성공을 나타내요. 예외는 /r(비파괴) 옵션을 쓴 치환(s///)과 음역(y///)인데, 이 경우 반환값이 치환의 결과가 돼요.
오른쪽 인자가 검색 패턴·치환·음역이 아니라 표현식이라면, 런타임에 검색 패턴으로 해석돼요. 이는 그 내용이 두 번 보간된다는 뜻이라,
'\\' =~ q'\\';
는 정규식 엔진이 패턴 \를 컴파일하려다 문법 오류로 여기므로 옳지 않아요.
이진 !~는 =~와 똑같지만 반환값이 논리적으로 부정돼요. !~에 비파괴 치환(s///r)이나 음역(y///r)을 쓰면 문법 오류예요.
곱셈 연산자 (Multiplicative Operators)
이진 *는 두 숫자를 곱하고, 이진 /는 두 숫자를 나눠요. 이진 %는 모듈로 연산자로, 첫 인자를 둘째 인자로 나눈 나머지를 계산해요. 정수 피연산자 $m과 $n이 주어질 때: $n이 양수면 $m % $n은 $m에서 $m 이하의 $n의 가장 큰 배수를 뺀 값이에요. $n이 음수면 $n의 가장 작은 배수 중 $m보다 작지 않은 것을 뺀 값이죠(즉 결과가 0 이하). use integer가 범위 안에 있으면 %는 C 컴파일러가 구현한 모듈로 연산자에 직접 접근하게 해줘요. 음수 피연산자에 대해선 덜 잘 정의되지만 더 빨리 실행돼요.
이진 x는 반복 연산자예요. 스칼라 문맥에서, 또는 왼쪽 피연산자가 괄호로 감싸이지 않았고 qw// 목록도 아니면, 문자열 반복을 수행해요. 그 경우 왼쪽 피연산자에 스칼라 문맥을 공급하고, 왼쪽 피연산자 문자열을 오른쪽 피연산자가 지정한 횟수만큼 반복한 문자열을 반환해요. x가 리스트 문맥이고 왼쪽 피연산자가 괄호로 감싸이거나 qw// 목록이면 리스트 반복을 수행해요. 오른쪽 피연산자가 0이나 음수면(음수는 경고) 문맥에 따라 빈 문자열이나 빈 목록을 반환해요.
print '-' x 80; # print row of dashes
print "\t" x ($tab/8), ' ' x ($tab%8); # tab over
@ones = (1) x 80; # a list of 80 1's
@ones = (5) x @ones; # set all elements to 5
가산 연산자 (Additive Operators)
이진 +는 두 숫자의 합을, 이진 -는 두 숫자의 차를 반환해요. 이진 .는 두 문자열을 연결해요.
시프트 연산자 (Shift Operators)
이진 <<는 왼쪽 인자를 오른쪽 인자가 지정한 비트 수만큼 왼쪽으로 시프트한 값을 반환해요. 인자는 정수여야 해요. 이진 >>는 오른쪽 시프트를 해요. use integer가 효과 중이면 부호 있는 C 정수가 쓰이고(산술 시프트), 그렇지 않으면 부호 없는 C 정수가 쓰여요(논리 시프트), 음수 피연산자도요. 어느 쪽이든 구현은 Perl이 빌드된 정수 타입 크기(32비트 또는 64비트)보다 큰 결과를 만들지 않아요. 음수 비트 수로 시프트하면 역방향 시프트예요: 왼쪽 시프트가 오른쪽 시프트로, 오른쪽이 왼쪽으로. 이는 음수 시프트가 정의되지 않는 C와 달라요. 정수 크기 이상의 값으로 시프트하면 0이 돼요(모든 비트가 떨어져 나가요), use integer 아래에서 음수를 그 정도 시프트해 -1이 되는 경우만 빼고요.
플랫폼 고유 정수에 지쳤다면 use bigint 프래그마가 문제를 아주 깔끔하게 우회해요:
print 20 << 20; # 20971520
print 20 << 32; # 0 with 32-bit integer,
# 85899345920 with 64-bit integer
use bigint;
print 20 << 100; # 25353012004564588029934064107520
이름 있는 단항 연산자 (Named Unary Operators)
여러 이름 있는 단항 연산자는 선택적 괄호가 있는 단일 인자의 함수로 취급돼요. 어떤 리스트 연산자(print() 등)나 단항 연산자(chdir() 등) 뒤에 다음 토큰으로 왼쪽 괄호가 오면 그 연산자와 괄호 안 인자는 일반 함수 호출처럼 가장 높은 우선순위로 취급돼요. 이름 있는 단항 연산자는 ||보다 높은 우선순위이므로:
chdir $foo || die; # (chdir $foo) || die
chdir($foo) || die; # (chdir $foo) || die
chdir ($foo) || die; # (chdir $foo) || die
chdir +($foo) || die; # (chdir $foo) || die
하지만 *는 이름 있는 연산자보다 높은 우선순위이므로:
chdir $foo * 20; # chdir ($foo * 20)
chdir($foo) * 20; # (chdir $foo) * 20
chdir ($foo) * 20; # (chdir $foo) * 20
chdir +($foo) * 20; # chdir ($foo * 20)
rand 10 * 20; # rand (10 * 20)
rand(10) * 20; # (rand 10) * 20
rand (10) * 20; # (rand 10) * 20
rand +(10) * 20; # rand (10 * 20)
우선순위에 관해, -f, -M 같은 파일테스트 연산자는 이름 있는 단항 연산자처럼 취급되지만 이 함수적 괄호 규칙을 따르지 않아요. 즉 -f($file).".bak"는 -f "$file.bak"와 동등하다는 뜻이에요.
클래스 인스턴스 연산자 (Class Instance Operator)
이진 isa는 왼쪽 인자가 오른쪽 인자가 준 클래스(또는 그 클래스에서 파생된 서브클래스)의 객체 인스턴스일 때 참으로 평가돼요. 왼쪽 인자가 정의되지 않았거나, bless된 객체 인스턴스가 아니고, 오른쪽 인자가 준 클래스에서 파생되지 않으면 거짓으로 평가돼요. 오른쪽 인자는 bareword나 문자열 클래스 이름을 내놓는 스칼라 표현식으로 클래스를 줄 수 있어요:
if ( $obj isa Some::Class ) { ... }
if ( $obj isa "Different::Class" ) { ... }
if ( $obj isa $name_of_class ) { ... }
이 기능은 use feature 'isa'로 켜면 Perl 5.31.6부터 사용할 수 있어요. 현재 범위에서 use v5.36(또는 그 이상) 선언이 하면 자동으로 켜져요.
관계 연산자 (Relational Operators)
참 또는 거짓을 반환하는 Perl 연산자는 대체로 안전하게 숫자로 쓸 수 있는 값을 반환해요. 예를 들어 이 섹션의 관계 연산자와 다음 섹션의 동등 연산자는 참에 1을, 정의된 빈 문자열 ""의 특수 버전을 거짓에 반환해요. 이 특수 버전은 0으로 세지만 "0 but true"처럼 부적절한 숫자 변환 경고는 면제돼요.
이진 <는 왼쪽 인자가 숫자로 오른쪽보다 작으면 참, >는 크면 참, <=는 작거나 같으면, >=는 크거나 같으면 참이에요. lt는 문자열로 왼쪽이 오른쪽보다 작으면, gt는 크면, le는 작거나 같으면, ge는 크거나 같으면 참이에요.
"$x < $y <= $z" 같은 관계 연산자 시퀀스는 연쇄 비교를 수행해요. 이들은 더 낮은 우선순위의 동등 연산자와는 연쇄하지 않는다는 점에 주의하세요. lt, le, ge, gt, cmp는 정렬(collation)을 포함한 use locale 형태가 효과 중이면 현재 LC_COLLATE 로케일이 지정한 정렬 순서를 사용해요. perllocale 참고.
대소문자 무시 비교에는 Perl v5.16 이상의 fc 케이스 폴딩 함수를 보세요:
if ( fc($x) eq fc($y) ) { ... }
동등 연산자 (Equality Operators)
이진 ==는 왼쪽 인자가 숫자로 오른쪽과 같으면, !=는 같지 않으면 참이에요. eq는 문자열로 같으면, ne는 같지 않으면 참이에요. "$x == $y == $z" 같은 동등 연산자 시퀀스는 연쇄 비교를 수행하며, 더 높은 우선순위의 관계 연산자와는 연쇄하지 않아요.
이진 <=>(spaceship)는 왼쪽 인자가 오른쪽보다 숫자로 작으면 -1, 같으면 0, 크면 1을 반환해요. 플랫폼이 NaN(not-a-number)을 숫자 값으로 지원한다면, <=>에 쓰면 undef를 반환해요. NaN은 <, ==, >, <=, >= 무엇과도 같지 않아서(심지어 NaN과도) 그 5개는 거짓을 반환해요. NaN != NaN은 참이에요.
$ perl -le '$x = "NaN"; print "No NaN support here" if $x == $x'
$ perl -le '$x = "NaN"; print "NaN support here" if $x != $x'
이진 cmp는 왼쪽 인자가 문자열로 오른쪽보다 작으면 -1, 같으면 0, 크면 1을 반환해요. <=>와 cmp의 차이:
print 10 <=> 2 #prints 1
print 10 cmp 2 #prints -1
이진 ~~는 인자들 사이에 스마트매치를 수행해요. 양방향 정렬 연산자 <=>와 cmp, 그리고 스마트매치 연산자 ~~는 서로에 대해, 그리고 같은 우선순위의 동등 연산자에 대해 비결합이에요.
스마트매치 연산자 (Smartmatch Operator)
smartmatch 기능은 새 코드에는 권장되지 않고 하위 호환을 위해 유지돼요. 스마트매치 연산자는 5.10.0에서 도입됐고 5.10.1에서 큰 변화가 있었어요. 기본으로, 그리고 5.40까지의 모든 피처 번들에서 켜져 있어요. 더 나중의 피처 번들과 스마트매치를 쓰려면 명시적으로 켜야 해요:
use v5.42;
use feature "smartmatch";
이진 ~~는 인자 사이에 "스마트매치"를 해요. 이는 대부분 perlsyn에 설명된 when 구조에서 암묵적으로 쓰여요(모든 when 절이 스마트매치 연산자를 호출하진 않지만요). Perl 연산자들 중 유일하게 스마트매치 연산자는 재귀할 수 있어요.
또한 유일하게, 다른 모든 Perl 연산자는 피연산자에 문맥(보통 문자열이나 숫자 문맥)을 강제하고 자동 변환하는 반면, 스마트매치는 피연산자의 실제 타입에서 문맥을 추론하고 그 타입 정보로 적절한 비교 메커니즘을 골라요.
~~ 연산자는 피연산자를 "다형적으로" 비교해요. 실제 타입(숫자·문자열·배열·해시 등)에 따라 비교 방법을 결정하죠. 같은 우선순위를 공유하는 동등 연산자처럼 참엔 1, 거짓엔 ""을 반환해요. 종종 "in", "inside of", "is contained in"으로 읽는 게 좋아요. 왼쪽 피연산자가 종종 오른쪽 피연산자 안에서 찾아지거든요. 그래서 스마트매치 피연산자의 순서가 일반 매치 연산자와 반대인 경우가 많아요. 즉 "더 작은" 것이 보통 왼쪽에, 더 큰 것이 오른쪽에 놓여요.
스마트매치의 동작은 인자가 어떤 타입이냐에 따라 결정되며, 다음 표로 결정돼요. 표에서 타입이 적용되는 첫 행이 스마트매치 동작을 결정해요. 실제로 일어나는 일은 대부분 둘째 연산자의 타입에 의해 결정되므로, 표는 왼쪽이 아니라 오른쪽 연산자를 기준으로 정렬돼 있어요.
Left Right Description and pseudocode
===============================================================
Any undef check whether Any is undefined
like: !defined Any
Any Object invoke ~~ overloading on Object, or die
Right operand is an ARRAY:
Left Right Description and pseudocode
===============================================================
ARRAY1 ARRAY2 recurse on paired elements of ARRAY1 and ARRAY2[2]
like: (ARRAY1[0] ~~ ARRAY2[0])
&& (ARRAY1[1] ~~ ARRAY2[1]) && ...
HASH ARRAY any ARRAY elements exist as HASH keys
like: grep { exists HASH->{$_} } ARRAY
Regexp ARRAY any ARRAY elements pattern match Regexp
like: grep { /Regexp/ } ARRAY
undef ARRAY undef in ARRAY
like: grep { !defined } ARRAY
Any ARRAY smartmatch each ARRAY element[3]
like: grep { Any ~~ $_ } ARRAY
Right operand is a HASH:
Left Right Description and pseudocode
===============================================================
HASH1 HASH2 all same keys in both HASHes
like: keys HASH1 ==
grep { exists HASH2->{$_} } keys HASH1
ARRAY HASH any ARRAY elements exist as HASH keys
like: grep { exists HASH->{$_} } ARRAY
Regexp HASH any HASH keys pattern match Regexp
like: grep { /Regexp/ } keys HASH
undef HASH always false (undef cannot be a key)
like: 0 == 1
Any HASH HASH key existence
like: exists HASH->{Any}
Right operand is CODE:
Left Right Description and pseudocode
===============================================================
ARRAY CODE sub returns true on all ARRAY elements[1]
like: !grep { !CODE->($_) } ARRAY
HASH CODE sub returns true on all HASH keys[1]
like: !grep { !CODE->($_) } keys HASH
Any CODE sub passed Any returns true
like: CODE->(Any)
Right operand is a Regexp:
Left Right Description and pseudocode
===============================================================
ARRAY Regexp any ARRAY elements match Regexp
like: grep { /Regexp/ } ARRAY
HASH Regexp any HASH keys match Regexp
like: grep { /Regexp/ } keys HASH
Any Regexp pattern match
like: Any =~ /Regexp/
Other:
Left Right Description and pseudocode
===============================================================
Object Any invoke ~~ overloading on Object,
or fall back to...
Any Num numeric equality
like: Any == Num
Num nummy[4] numeric equality
like: Num == nummy
undef Any check whether undefined
like: !defined(Any)
Any Any string equality
like: Any eq Any
참고:
- 빈 해시나 배열은 매치돼요.
- 즉 각 요소가 다른 배열의 같은 인덱스 요소를 스마트매치해요.
- 순환 참조를 찾으면 참조 동등성으로 폴백해요.
- 실제 숫자이거나 숫자처럼 보이는 문자열.
스마트매치는 bless되지 않은 해시·배열 참조를 암묵적으로 역참조하므로, 그 경우 HASH와 ARRAY 항목이 적용돼요. bless된 참조에는 Object 항목이 적용돼요. 해시를 포함한 스마트매치는 해시 키만 고려하지 절대 해시 값을 고려하지 않아요.
"like" 코드 항목이 항상 정확한 표현은 아니에요. 예를 들어 스마트매치 연산자는 가능할 때마다 단락하지만 grep은 그러지 않아요. 또 스칼라 문맥의 grep은 매치 수를 반환하지만 ~~는 참·거짓만 반환해요.
대부분의 연산자와 달리 스마트매치 연산자는 undef를 특별히 대우할 줄 알아요:
use v5.10.1;
@array = (1, 2, 3, undef, 4, 5);
say "some elements undefined" if undef ~~ @array;
각 피연산자는 수정된 스칼라 문맥에서 고려되는데, 배열·해시 변수가 연산자에게 참조로 전달되어 암묵적으로 역참조된다는 수정이에요. 각 쌍의 두 요소는 같아요:
use v5.10.1;
my %hash = (red => 1, blue => 2, green => 3,
orange => 4, yellow => 5, purple => 6,
black => 7, grey => 8, white => 9);
my @array = qw(red blue green);
say "some array elements in hash keys" if @array ~~ %hash;
say "some array elements in hash keys" if \@array ~~ \%hash;
say "red in array" if "red" ~~ @array;
say "red in array" if "red" ~~ \@array;
say "some keys end in e" if /e$/ ~~ %hash;
say "some keys end in e" if /e$/ ~~ \%hash;
첫 배열의 각 요소가 둘째 배열의 대응 요소를 (재귀적으로) 스마트매치하면 두 배열은 스마트매치돼요:
use v5.10.1;
my @little = qw(red blue green);
my @bigger = ("red", "blue", [ "orange", "green" ] );
if (@little ~~ @bigger) { # true!
say "little is contained in bigger";
}
스마트매치 연산자가 중첩 배열을 재귀하므로 "red"가 배열에 있다고 계속 보고할 거예요:
use v5.10.1;
my @array = qw(red blue green);
my $nested_array = [[[[[[[ @array ]]]]]]];
say "red in array" if "red" ~~ $nested_array;
두 배열이 서로 스마트매치하면 각자의 값의 딥 카피라는 뜻이에요:
use v5.12.0;
my @a = (0, 1, 2, [3, [4, 5], 6], 7);
my @b = (0, 1, 2, [3, [4, 5], 6], 7);
if (@a ~~ @b && @b ~~ @a) {
say "a and b are deep copies of each other";
}
elsif (@a ~~ @b) {
say "a smartmatches in b";
}
elsif (@b ~~ @a) {
say "b smartmatches in a";
}
else {
say "a and b don't smartmatch each other at all";
}
한 해시를 다른 해시와 스마트매치하면 둘 다 같은 키를 가졌는지 보고해요. 그 이상도 그 이하도 아니죠. 두 레코드가 같은 필드 이름을 가졌는지, 값에는 신경 쓰지 않고 확인하는 데 쓸 수 있어요:
use v5.10.1;
sub make_dogtag {
state $REQUIRED_FIELDS = { name=>1, rank=>1, serial_num=>1 };
my ($class, $init_fields) = @_;
die "Must supply (only) name, rank, and serial number"
unless $init_fields ~~ $REQUIRED_FIELDS;
...
}
스마트매치 연산자는 대부분 when 절의 암묵적 연산자로 쓰여요. perlsyn의 "Switch Statements" 섹션 참고.
객체의 스마트매치 (Smartmatching of Objects)
객체의 밑바탕 표현에 의존하는 걸 피하기 위해, 스마트매치의 오른쪽 피연산자가 ~~를 오버로드하지 않는 객체면 Smartmatching a non-overloaded object breaks encapsulation 예외를 일으켜요. 무언가가 객체 "안에" 있는지 파고들어 볼 권리가 없기 때문이에요. ~~ 오버로드가 없는 객체에서는 이 모두가 불법이에요:
%hash ~~ $object
42 ~~ $object
"fred" ~~ $object
하지만 ~~ 연산자를 오버로드해 객체가 스마트매치되는 방식을 바꿀 수 있어요. 이것은 일반적인 스마트매치 의미를 확장하는 것이 허용돼요. ~~ 오버로드가 있는 객체는 overload 참고.
제법 유용하진 않지만 객체를 왼쪽 피연산자로 쓰는 건 허용돼요. 스마트매치 규칙이 오버로딩보다 우선해서, 왼쪽 피연산자의 객체가 스마트매치 오버로딩을 가져도 무시돼요. 오버로드되지 않은 객체인 왼쪽 피연산자는 ref 연산자가 반환하는 무엇이든 문자열이나 숫자 비교로 폴백해요. 즉 $object ~~ X는 X를 인자로 오버로드 메서드를 호출하지 않아요. 대신 위 표를 정상적으로 보고, X의 타입에 따라 오버로딩이 호출될 수도 있고 아닐 수도 있어요. 단순 문자열이나 숫자에 대해선 "in"이 다음과 동등해져요:
$object ~~ $number ref($object) == $number
$object ~~ $string ref($object) eq $string
비트 AND (Bitwise And)
이진 &는 피연산자를 비트 단위로 AND한 결과를 반환해요. 관계 연산자보다 낮은 우선순위라서, 예를 들어 이런 검사에서 괄호가 필수예요:
print "Even\n" if ($x & 1) == 0;
bitwise 기능이 켜져 있으면 이 연산자는 항상 피연산자를 숫자로 취급해요.
비트 OR와 배타적 OR (Bitwise Or and Exclusive Or)
이진 |는 피연산자를 비트 단위로 OR해요. 대응 비트가 둘 다 0이면 결과 비트는 0, 둘 중 하나가 1이면 결과는 1이에요. 이진 ^는 비트 단위 XOR을 해요. 둘 다 0이거나 둘 다 1이면 0, 딱 하나만 1이면 1이에요. |와 ^는 관계 연산자보다 낮은 우선순위라 괄호가 필수인 검사가 있어요:
print "false\n" if (8 | 2) != 10;
C 스타일 논리 AND (C-style Logical And)
이진 &&는 단락 논리 AND 연산을 수행해요. 즉 왼쪽 피연산자가 거짓이면 오른쪽 피연산자는 아예 평가되지 않아요. &&는 마지막으로 평가된 값을 반환해요(0이나 1을 반환하는 C의 &&와 달라요). 제어 흐름에서 &&의 대안으로 Perl은 and 연산자를 제공해요. 단락 동작은 동일하고, and의 우선순위는 훨씬 낮아서 괄호 없이 리스트 연산자 뒤에서 안전하게 쓸 수 있어요.
C 스타일 논리 OR, XOR, 정의된 OR (C-style Logical Or, Xor, and Defined Or)
이진 ||는 단락 논리 OR 연산을 수행해요. 즉 왼쪽 피연산자가 참이면 오른쪽은 평가되지 않아요. 제어 흐름에서 ||의 대안으로 or 연산자가 있어요. 우선순위가 훨씬 낮아 괄호 없이 리스트 연산자 뒤에서 안전하게 쓸 수 있어요:
unlink "alpha", "beta", "gamma"
or gripe(), next LINE;
C 스타일 연산자로는 이렇게 썼을 거예요:
unlink("alpha", "beta", "gamma")
|| (gripe(), next LINE);
이진 ^^는 논리 XOR 연산을 해요. 두 피연산자가 모두 평가되고 결과는 정확히 하나의 피연산자가 참일 때만 참이에요.
C에 직접적인 동등물은 없지만, Perl의 // 연산자는 C 스타일 "or"와 관련돼 있어요. 사실 진리값 대신 왼쪽의 정의됨(definedness)을 검사한다는 점만 빼고 ||와 정확히 같아요. 그래서 EXPR1 // EXPR2는 EXPR1이 정의됐으면 그 값을, 아니면 EXPR2의 값을 반환해요. 이는 변수에 기본값을 제공하는 데 매우 유용해요. $x와 $y 중 하나라도 정의되는지 실제로 검사하고 싶다면 defined($x // $y)를 쓰세요.
||와 //는 마지막으로 평가된 값을 반환해요. 그래서 홈 디렉토리를 찾는 합리적으로 이식 가능한 방법은:
$home = $ENV{HOME}
// $ENV{LOGDIR}
// (getpwuid($<))[7]
// die "You're homeless!\n";
특히 두 집합을 골라 할당하는 데는 쓰지 마세요:
@a = @b || @c; # This doesn't do the right thing
@a = scalar(@b) || @c; # because it really means this.
@a = @b ? @b : @c; # This works fine, though.
범위 연산자 (Range Operators)
이진 ..는 범위 연산자로, 문맥에 따라 정말 두 가지 다른 연산자예요. 리스트 문맥에서는 왼쪽 값에서 오른쪽 값까지 (1씩 올라가며) 세는 값의 목록을 반환해요. 왼쪽 값이 오른쪽 값보다 크면 빈 목록을 반환하죠. foreach (1..10) 루프와 배열 슬라이스에 유용해요. foreach 루프에서 범위 연산자를 표현식으로 쓰면 임시 배열이 만들어지지 않아요. 범위 연산자는 마법적 자동 증가를 이용해 문자열에서도 동작해요.
스칼라 문맥에서 ..는 불리언 값을 반환해요. 이 연산자는 플립플롭처럼 쌍안정(bistable)이고, sed, awk, 여러 편집기의 줄 범위(쉼표) 연산자를 흉내 내요. 각 .. 연산자는 그것을 포함한 서브루틴 호출을 가로질러서도 자기만의 불리언 상태를 유지해요. 왼쪽 피연산자가 거짓인 동안은 거짓이에요. 왼쪽이 참이 되면, 오른쪽이 참이 될 때까지 참으로 유지되고, 그 후에 다시 거짓이 돼요. 참이 된 평가와 같은 평가에서 오른쪽을 검사하고 거짓이 될 수 있지만(awk처럼), 그래도 한 번은 참을 반환해요. sed처럼 오른쪽을 다음 평가까지 검사하지 않으려면 두 점 대신 세 점(...)을 쓰세요. 그 외 모든 점에서 ...는 ..처럼 동작해요.
범위의 반환값은 거짓용으로 빈 문자열이거나, 참용으로 시퀀스 번호(1부터 시작)예요. 마지막 시퀀스 번호에는 문자열 E0이 붙는데, 숫자 값엔 영향이 없지만 끝점을 제외하고 싶을 때 검색할 무언가를 줘요. 스칼라 ..의 한쪽 피연산자가 상수 표현식이면, 그 피연산자는 현재 입력 줄 번호($. 변수)와 같으면 참으로 여겨져요.
예시 — 스칼라 연산자:
if (101 .. 200) { print; } # print 2nd hundred lines, short for
# if ($. == 101 .. $. == 200) { print; }
next LINE if 1 .. /^$/; # skip header lines, short for
# next LINE if $. == 1 .. /^$/;
# (typically in a loop labeled LINE)
s/^/> / if /^$/ .. eof(); # quote body
# parse mail messages
while (<>) {
$in_header = 1 .. /^$/;
$in_body = /^$/ .. eof;
if ($in_header) {
# do something
} else { # in body
# do something else
}
} continue {
close ARGV if eof; # reset $. each file
}
두 범위 연산자의 차이를 보여주는 간단한 예시:
@lines = (" - Foo",
"01 - Bar",
"1 - Baz",
" - Quux");
foreach (@lines) {
if (/0/ .. /1/) {
print "$_\n";
}
}
이 프로그램은 "Bar"를 담은 줄만 출력해요. 범위 연산자를 ...로 바꾸면 "Baz" 줄도 출력해요.
리스트 연산자 예시 몇 가지:
for (101 .. 200) { print } # print $_ 100 times
@foo = @foo[0 .. $#foo]; # an expensive no-op
@foo = @foo[$#foo-4 .. $#foo]; # slice last 5 items
각 피연산자가 정수 형태로 평가되므로 리스트 문맥에서 2.18 .. 3.14는 두 요소를 반환해요:
@list = (2.18 .. 3.14); # same as @list = (2 .. 3);
두 피연산자가 둘 다 문자열이면 리스트 문맥의 범위 연산자는 마법적 자동 증가 알고리즘을 쓸 수 있어요. 다음 규칙을 따릅니다:
- 한 가지 예외를 빼면, 두 문자열이 Perl에 숫자처럼 보이면 마법 증가가 적용되지 않고 문자열이 숫자(정확히는 정수)로 취급돼요. 예를 들어
"-2".."2"는-2..2와 같고,"2.18".."3.14"는2, 3을 만듭니다. - 위 규칙의 예외는 왼쪽 문자열이
0으로 시작하고 한 글자보다 길 때예요. 그 경우"01"같은 문자열이 보통 Perl에 숫자처럼 보여도 마법 증가가 적용돼요. 예를 들어"01".."04"는"01", "02", "03", "04"를,"00".."-1"은"00"부터"99"까지를 만듭니다. 앞에 0이 붙은 날짜를 얻으려면:
@z2 = ("01" .. "31");
print $z2[$mday];
문자열을 숫자로 강제하려면:
@numbers = ( 0+$first .. 0+$last );
- 지정한 초기 값이 마법 증가 시퀀스의 일부가 아니면(즉
/^[a-zA-Z]*[0-9]*\z/에 매치되는 비어 있지 않은 문자열이 아니면) 초기 값만 반환돼요. 예를 들어"ax".."az"는"ax", "ay", "az"지만"*x".."az"는"*x"만 만듭니다. - 마법 증가 규칙을 따르는 다른 문자열 초기 값에 대해선 대응 시퀀스가 반환돼요. 예를 들어
@alphabet = ("A" .. "Z");로 알파벳 전부를,$hexdigit = (0 .. 9, "a" .. "f")[$num & 15];로 16진수 숫자를 얻을 수 있어요. - 지정한 최종 값이 마법 증가가 만들 시퀀스에 없으면, 다음 값이 지정한 최종 값보다 길어질 때까지 갑니다. 최종 문자열 길이가 첫 번째보다 짧으면 빈 목록이 반환돼요. 예를 들어
"a".."--"는"a".."zz"와 같고,"0".."xx"는"0"부터"99"까지,"aaa".."--"는 빈 목록이에요.
조건 연산자 (Conditional Operator)
삼항 ?:는 C에서처럼 조건 연산자예요. if-then-else처럼 동작해요. ? 앞의 인자가 참이면 : 앞의 인자를, 아니면 : 뒤의 인자를 반환해요. 예시:
printf "I have %d dog%s.\n", $n,
($n == 1) ? "" : "s";
스칼라 또는 리스트 문맥은 선택된 2번째 또는 3번째 인자로 아래로 전파돼요:
$x = $ok ? $y : $z; # get a scalar
@x = $ok ? @y : @z; # get an array
$x = $ok ? @y : @z; # oops, that's just a count!
2번째와 3번째 인자가 둘 다 유효한 lvalue면 이 연산자에 할당할 수 있어요:
($x_or_y ? $x : $y) = $z;
이 연산자는 할당 가능한 결과를 만들므로, 괄호 없이 할당을 쓰면 곤란해져요. 예를 들어 $x % 2 ? $x += 10 : $x += 2는 사실 (($x % 2) ? ($x += 10) : $x) += 2를 의미해요. 더 단순하게는 $x += ($x % 2) ? 10 : 2;로 쓰는 게 좋아요.
할당 연산자 (Assignment Operators)
=는 일반적인 할당 연산자예요. 할당 연산자는 C에서처럼 동작해요. 즉 $x += 2;는 $x = $x + 2;와 동등해요(다만 tie()처럼 lvalue 역참조가 촉발하는 부수 효과는 중복되지 않아요). 인식되는 것들은:
**= += *= &= &.= <<= &&=
-= /= |= |.= >>= ||=
.= %= ^= ^.= //=
x= ^^=
이들은 가족별로 묶였지만 모두 할당의 우선순위를 가져요. 이 결합 할당 연산자는 스칼라에만 동작할 수 있고, 일반 할당 연산자는 배열·해시·목록·참조에도 할당할 수 있어요.
스칼라 할당 연산자는 왼쪽 피연산자, 즉 할당된 스칼라를 반환해요. C와 달리 유효한 lvalue를 만듭니다. 할당을 수정하는 것은 할당 후 할당된 변수를 수정하는 것과 동등해요. 복사본을 수정하는 데 유용해요:
($tmp = $global) =~ tr/13579/24680/;
5.14부터는 이렇게도 가능해요:
use v5.14;
$tmp = ($global =~ tr/13579/24680/r);
마찬가지로 ($x += 2) *= 3;은 $x += 2; $x *= 3;과 동등해요. 세 개의 점이 찍힌 비트 할당 연산자(&.=, |.=, ^.=)는 Perl 5.22의 새것이에요.
쉼표 연산자 (Comma Operator)
이진 ,는 쉼표 연산자예요. 스칼라 문맥에서 왼쪽 인자를 평가해 그 값을 버리고, 그다음 오른쪽 인자를 평가해 그 값을 반환해요. C의 쉼표 연산자와 똑같아요. 리스트 문맥에서는 그냥 리스트 인자 구분자이고, 두 인자를 목록에 삽입해요. 인자들도 왼쪽에서 오른쪽으로 평가돼요.
=> 연산자(때로 "fat comma"라고 발음)는 쉼표의 동의어인데, 왼쪽의 단어가 문자나 밑줄로 시작하고 문자·숫자·밑줄로만 구성되어 있으면 문자열로 해석되게 해요. 연산자·상수·단일 숫자 v-string·함수 호출로 해석될 피연산자도 포함돼요. 의심되면 왼쪽 피연산자를 명시적으로 따옴표로 감쌀 수 있어요. 예:
use constant FOO => "something";
my %h = ( FOO => 23 );
이는 my %h = ("FOO", 23);과 동등해요. my %h = ("something", 23);은 아니에요. =>는 해시에서 키·값의 대응, 목록의 다른 짝 요소들을 문서화하는 데 도움이 돼요:
%hash = ( $key => $value );
login( $username => $password );
특수 따옴표 동작은 우선순위를 무시하므로 왼쪽 피연산자의 일부에 적용될 수 있어요.
리스트 연산자 (List Operators (Rightward))
리스트 연산자 오른쪽에서 쉼표는 매우 낮은 우선순위라, 거기 있는 모든 쉼표 구분 표현식을 제어해요. 더 낮은 우선순위의 연산자는 논리 연산자 and, or, not뿐이라, 괄호 없이 리스트 연산자 호출을 평가하는 데 쓸 수 있어요:
open HANDLE, "< :encoding(UTF-8)", "filename"
or die "Can't open: $!\n";
논리 NOT (Logical Not)
단항 not은 오른쪽 표현식의 논리 부정을 반환해요. 매우 낮은 우선순위라는 점만 빼고 !와 동등해요.
논리 AND (Logical And)
이진 and는 두 주변 표현식의 논리 결합을 반환해요. 매우 낮은 우선순위라는 점만 빼고 &&와 동등해요. 단락한다는 뜻이에요: 오른쪽 표현식은 왼쪽이 참일 때만 평가돼요.
논리 OR와 배타적 OR (Logical or and Exclusive Or)
정의된 OR를 위한 낮은 우선순위 연산자는 없어요. 이진 or는 두 주변 표현식의 논리 포괄적 분리를 반환해요. 매우 낮은 우선순위라는 점만 빼고 ||와 동등해요. 그래서 제어 흐름에 유용해요:
print FH $data or die "Can't write to FH: $!";
단락하므로 오른쪽 표현식은 왼쪽이 거짓일 때만 평가돼요. 우선순위 때문에 ||의 대체품으로 쓰지 않도록 조심해야 해요:
$x = $y or $z; # bug: this is wrong
($x = $y) or $z; # really means this
$x = $y || $z; # better written this way
하지만 리스트 문맥 할당에서 ||를 제어 흐름에 쓰려 한다면, 할당이 더 높은 우선순위를 갖도록 or가 필요할 거예요:
@info = stat($file) || die; # oops, scalar sense of stat!
@info = stat($file) or die; # better, now @info gets its due
이진 xor는 두 주변 표현식의 논리 배타적 분리를 반환해요. 둘 중 하나(둘 다는 아닌)가 참이면 true를 반환해요. 매우 낮은 우선순위라는 점만 빼고 ^^와 동등해요. 당연히 단락할 수 없어요. 두 상호배타적 조건이 실제로 상호배타적인지 확인하는 데 쓰이는 경향이 있어요:
($x =~ qr/$pat/ xor $x !~ qr/$pat/) or die;
C 연산자 중 Perl에 없는 것 (C Operators Missing From Perl)
C에 있고 Perl에 없는 것은:
- 단항
&— 주소 연산자. (참조를 얻는\연산자 참고.) - 단항
*— 역참조 주소 연산자. (Perl의 프리픽스 역참조 연산자는 타입이 지정돼 있어요:$,@,%,&.) (TYPE)— 타입 캐스팅 연산자.
따옴표와 따옴표류 연산자 (Quote and Quote-like Operators)
보통 따옴표를 리터럴 값으로 생각하지만, Perl에서는 연산자로 기능해요. 다양한 종류의 보간과 패턴 매칭 능력을 제공하죠. Perl은 이런 동작에 관례적인 따옴표 문자를 제공하지만, 어떤 것에 대해서도 자기가 원하는 따옴표 문자를 고를 수 있는 방법을 제공해요. 다음 표에서 {}는 여러분이 고른 임의의 한 쌍의 구분자를 나타내요.
Customary Generic Meaning Interpolates
'' q{} Literal no
"" qq{} Literal yes
`` qx{} Command yes*
qw{} Word list no
// m{} Pattern match yes*
qr{} Pattern yes*
s{}{} Substitution yes*
tr{}{} Transliteration no (but see below)
y{}{} Transliteration no (but see below)
<<EOF here-doc yes*
* unless the delimiter is ''.
괄호 아닌 구분자는 앞뒤에 같은 문자를 쓰지만, 네 종류의 ASCII 괄호(둥근·각·사각·중괄호)는 모두 중첩돼요. 즉 q{foo{bar}baz}는 'foo{bar}baz'와 같아요. 다만 Perl 코드를 따옴표로 감쌀 때는 항상 되는 건 아니라는 점에 주의하세요. $s = q{ if ($x eq "}") ... };는 문법 오류예요. Text::Balanced 모듈(v5.8부터 표준)이 이를 제대로 할 수 있어요.
extra_paired_delimiters 기능이 켜져 있으면 Perl은 다양한 유니코드 문자들을 쌍으로 인식해요. 전체 목록은 이 문서 끝의 "List of Extra Paired Delimiters"를 참고하세요.
연산자와 따옴표 문자 사이에 공백이 있을 수 있어요(그리고 어떤 경우엔 있어야 해요), 단 #을 따옴표 문자로 쓸 때는 제외예요. q#foo#는 문자열 foo로 파싱되는 반면, q #foo#는 연산자 q 뒤에 주석이 오는 거예요. 그 인자는 다음 줄에서 가져와요. 이렇게 쓸 수 있게 해주죠:
s {foo} # Replace foo
{bar} # with bar.
따옴표 문자가 단어 문자(/\w/에 매치)일 때는 공백을 꼭 써야 해요:
q XfooX # Works: means the string 'foo'
qXfooX # WRONG!
보간하는 구조와 구분자가 단따옴표(')가 아닌 음역에서 사용할 수 있는 이스케이프 시퀀스는 다음과 같아요:
Sequence Note Description
\t tab (HT, TAB)
\n newline (NL)
\r return (CR)
\f form feed (FF)
\b backspace (BS)
\a alert (bell) (BEL)
\e escape (ESC)
\x{263A} [1,8] hex char (example shown: SMILEY)
\x{ 263A } Same, but shows optional blanks inside and
adjoining the braces
\x1b [2,8] restricted range hex char (example: ESC)
\N{name} [3] named Unicode character or character sequence
\N{U+263D} [4,8] Unicode character (example: FIRST QUARTER MOON)
\c[ [5] control char (example: chr(27))
\o{23072} [6,8] octal char (example: SMILEY)
\033 [7,8] restricted range octal char (example: ESC)
각 노트의 세부사항:
- [1] 중괄호 사이의 16진수 숫자가 지정하는 문자. 빈 칸(탭·공백)이 숫자를 한쪽 또는 양쪽 중괄호에서 분리할 수 있어요. 그 외엔 중괄호 사이에서는 16진수 숫자만 유효해요. 유효한 숫자가 없으면 생성 문자는 NULL 문자(
\x{00})예요. - [2] 0x00에서 0xFF 범위의 16진수 숫자가 지정하는 문자.
\x뒤에 2자리 미만의 유효 숫자가 오면 0 패딩돼요.\x7은\x07로, 고독한"\x"는\x00으로 해석돼요. - [3]
name이 주는 유니코드 문자 또는 문자 시퀀스.charnames참고. - [4]
\N{U+<16진수 숫자>}는 유니코드 코드 포인트가 그 숫자인 유니코드 문자. - [5]
\c뒤의 문자는 표에 맞게 다른 문자로 매핑돼요:
Sequence Value
\c@ chr(0)
\cA chr(1)
\ca chr(1)
\cB chr(2)
\cb chr(2)
...
\cZ chr(26)
\cz chr(26)
\c[ chr(27)
\c] chr(29)
\c^ chr(30)
\c_ chr(31)
\c? chr(127)
다시 말해, 대문자와 64를 xor한 코드 포인트를 가진 문자예요. \c?는 ASCII 플랫폼에서 DELETE인데 ord("?") ^ 64가 127이기 때문이고, \c@는 "@"의 ord가 64라 64 자체를 xor하면 0이 나와서 NULL이에요. 이스케이프된 구분자와 \\는 건너뛰어져요. \N{...}로 플랫폼 독립적인 제어 문자를 얻을 수 있어요.
- [6] 중괄호 사이의 8진수 숫자가 지정하는 문자.
- [7] 000에서 777 범위의 세 자리 8진수 숫자가 지정하는 문자. Perl 5.14부터는
\o{}를 쓸 수 있어요. - [8] 여러 구조가 숫자로 문자를 지정해요. 숫자가 255(0xFF, 0377) 이하이면 Perl은 플랫폼 고유 인코딩으로 해석해요. 256(0x100, 0400) 이상이면 유니코드 코드 포인트로 해석해요. 예외는
\N{U+<hex>}인데 항상 유니코드 코드 포인트로 해석돼요.
NOTE: C나 다른 언어와 달리 Perl에는 수직 탭(VT)을 위한
\v이스케이프가 없지만,\N{VT},\ck,\N{U+0b},\x0b를 쓸 수 있어요. (Perl 정규식 패턴에서\v는 의미가 있어요.perlre참고.)
보간하는 구조에서만 사용할 수 있고 음역에서는 사용할 수 없는 이스케이프:
\l lowercase next character only
\u titlecase (not uppercase!) next character only
\L lowercase all characters till \E or end of string
\U uppercase all characters till \E or end of string
\F foldcase all characters till \E or end of string
\Q quote (disable) pattern metacharacters till \E or
end of string
\E end either case modification or quoted section
(whichever was last seen)
\L, \U, \F, \Q는 쌓을 수 있고, 그 경우 각각에 \E가 하나씩 필요해요. 예:
say "This \Qquoting \ubusiness \Uhere isn't\E done yet,\E is it?";
This quoting Business HERE ISN'T done yet, is it?
모든 시스템은 가상의 "\n"을 줄 종결자("newline")로 씁니다. 불변하는 물리적 newline 문자 같은 것은 없어요. 운영체제·디바이스 드라이버·C 라이브러리·Perl이 모두 보존하려는 환상일 뿐이에요. "newline"을 뜻할 때는 "\n"을 쓰되, 정확한 문자를 필요로 할 때는 리터럴 ASCII를 쓰세요. 예를 들어 대부분의 네트워킹 프로토콜은 CR+LF("\015\012" 또는 "\cM\cJ")를 줄 종결자로 기대하고 선호해요.
보간하는 구조에서 "$"나 "@"로 시작하는 변수가 보간돼요. $a[3]이나 $href->{key}[0] 같은 첨자 변수, 배열·해시 슬라이스도 보간돼요. 하지만 $obj->meth 같은 메서드 호출은 보간되지 않아요. 배열이나 슬라이스를 보간하면 요소를 순서대로, $" 값으로 구분해 보간하므로 join $", @array를 보간하는 것과 동등해요.
이중 따옴표 문자열에서 \Q의 따옴표 처리는 보간과 이스케이프 처리 후에 적용돼요:
"abc\Qfoo\tbar$s\Exyz"
는 "abc" . quotemeta("foo\tbar$s") . "xyz"와 동등해요. 정규식 연산자(qr//, m//, s///)의 패턴에서는 \Q의 따옴표 처리가 보간 처리 후, 이스케이프 처리 전에 적용돼요.
더 단순한 따옴표류 연산자 (Simpler Quote-Like Operators)
q/STRING/ 또는 'STRING' — 단따옴표 리터럴 문자열. 백슬래시는 구분자나 다른 백슬래시가 뒤따르지 않으면 백슬래시를 나타내요. 그 경우 구분자나 백슬래시가 보간돼요.
$foo = q!I said, "You said, 'She said it.'"!;
$bar = q('This is it.');
$baz = '\n'; # a two-character string
qq/STRING/ 또는 "STRING" — 이중 따옴표 보간 문자열.
$_ .= qq
(*** The previous line contains the naughty word "$1".\n)
if /\b(tcl|java|python)\b/i; # :-)
$baz = "\n"; # a one-character string
qx/STRING/ 또는 `STRING` — (가능하면) 보간된 다음 시스템 명령으로 sh(또는 필요 시 그 동등물)를 통해 실행되는 문자열. 셸 와일드카드·파이프·리다이렉션이 존중돼요. 명령의 수집된 표준 출력이 반환돼요; 표준 오류는 영향받지 않아요. 스칼라 문맥에서는 단일(잠재적으로 여러 줄) 문자열로, 셸(또는 명령)을 시작할 수 없으면 undef로 돌아와요. 리스트 문맥에서는 줄 목록(출력 줄당 하나)을 반환해요.
print qx/date/; # prints "Sun Jan 28 06:16:19 CST 2024"
명령의 STDERR과 STDOUT을 함께 잡으려면 $output = cmd 2>&1;, STDOUT만 잡고 STDERR을 버리려면 $output = cmd 2>/dev/null; 등을 씁니다. 명령이 쓴 STDIN 파일핸들은 Perl의 STDIN에서 상속돼요. 단따옴표를 구분자로 쓰면 명령을 Perl의 이중 따옴표 보간으로부터 보호해 셸에 넘겨줘요:
$perl_info = qx(ps $$); # that's Perl's $$
$shell_info = qx'ps $$'; # that's the new shell's $$
system처럼 백틱은 자식 프로세스 종료 코드를 $?에 넣어요. use open 프래그마로 명령 출력을 읽을 때 쓸 I/O 레이어를 제어할 수 있어요. qx//는 readpipe 함수처럼 호출할 수도 있어요.
qw/STRING/ — STRING에서 추출한 단어 목록으로 평가돼요. 임베디드 공백이 단어 구분자예요. 대략 split(" ", q/STRING/);과 동등한데, ASCII 공백에서만 나누고, 컴파일 타임에 실제 목록을 생성하며, 스칼라 문맥에서 목록의 마지막 요소를 반환한다는 차이가 있어요. qw(foo bar baz)는 "foo", "bar", "baz"와 의미상 동등해요.
use POSIX qw( setlocale localeconv )
@EXPORT = qw( foo bar baz );
흔한 실수는 단어를 쉼표로 구분하려 하거나, 여러 줄 qw-문자열에 주석을 넣거나, 단어 사이 공백을 \-이스케이프하려는 거예요. 그 때문에 STRING에 ,, #, \ 문자가 있으면 use warnings 프래그마와 -w 스위치(즉 $^W 변수)가 경고를 내요.
정규식 따옴표류 연산자 (Regexp Quote-Like Operators)
qr/STRING/msixpodualn — 이 연산자는 STRING을 정규식으로 따옴표 처리(그리고 가능하면 컴파일)해요. STRING은 m/PATTERN/의 PATTERN과 같은 방식으로 보간돼요. '를 구분자로 쓰면 변수 보간이 안 돼요. 대응하는 /STRING/msixpodualn 표현식 대신 쓸 수 있는 Perl 값을 반환해요. 반환값은 원래 패턴의 정규화된 버전이에요. 같은 문자를 담은 문자열과 마법적으로 달라요: ref(qr/x/)는 "Regexp"를 반환해요.
$rex = qr/my.STRING/is;
print $rex; # prints (?si-xm:my.STRING)
s/$rex/foo/;
는 s/my.STRING/foo/is;와 동등해요. 결과는 매치의 서브패턴으로 쓸 수 있어요:
$re = qr/$pattern/;
$string =~ /foo${re}bar/; # can be interpolated in other
# patterns
$string =~ $re; # or used standalone
$string =~ /$re/; # or this way
아래 수식어(옵션)들:
m Treat string as multiple lines.
s Treat string as single line. (Make . match a newline)
i Do case-insensitive pattern matching.
x Use extended regular expressions; specifying two
x's means \t and the SPACE character are ignored within
square-bracketed character classes
p When matching preserve a copy of the matched string so
that ${^PREMATCH}, ${^MATCH}, ${^POSTMATCH} will be
defined (ignored starting in v5.20 as these are always
defined starting in that release)
o Compile pattern only once.
a ASCII-restrict: Use ASCII for \d, \s, \w and [[:posix:]]
character classes; specifying two a's adds the further
restriction that no ASCII character will match a
non-ASCII one under /i.
l Use the current run-time locale's rules.
u Use Unicode rules.
d Use Unicode or native charset, as in 5.12 and earlier.
n Non-capture mode. Don't let () fill in $1, $2, etc...
/a, /d, /l, /u 수식어(Perl 5.14에서 추가)는 문자셋 규칙을 제어하지만, 명시적으로 지정하고 싶을 만한 건 /a뿐이에요. 자세한 내용은 perlre 참고.
m/PATTERN/msixpodualngc 또는 /PATTERN/msixpodualngc — 문자열에서 패턴 매치를 검색하고, 스칼라 문맥에서 성공하면 참, 실패하면 거짓을 반환해요. =~나 !~로 문자열을 지정하지 않으면 $_ 문자열이 검색돼요. 추가 매치 처리 수식어:
g Match globally, i.e., find all occurrences.
c Do not reset search position on a failed match when /g is
in effect.
/가 구분자면 앞의 m은 선택 사항이에요. ?가 구분자면 match-once 규칙이 적용돼요. '(단따옴표)가 구분자면 PATTERN에 변수 보간이 수행되지 않아요. PATTERN은 매치가 평가될 때마다 보간될 변수를 포함할 수 있어요(구분자가 단따옴표일 때는 제외). Perl은 포함된 보간 변수가 바뀌지 않는 한 패턴을 재컴파일하지 않아요. 끝 구분자 뒤에 /o("once")를 추가해 검사를 건너뛰고 재컴파일을 못 하게 강제할 수 있어요.
빈 패턴 // — PATTERN이 빈 문자열로 평가되면, 현재 동적 범위에서 마지막으로 성공적으로 매치된 정규식이 대신 사용돼요(perlvar의 "Scoping Rules of Regex Variables" 참고). 이 경우 빈 패턴의 g와 c 플래그만 존중되고, 다른 플래그는 원래 패턴에서 가져와요. //(빈 정규식)을 //(defined-or 연산자)로 Perl이 착각하게 만들 수 있으니, 애매하면 괄호나 공백, 또는 앞에 m을 붙여(m//) 명확히 하세요.
리스트 문맥 매치 — /g 옵션을 쓰지 않으면 리스트 문맥의 m//는 패턴의 괄호가 매치한 하위 표현식 목록(즉 $1, $2, $3...)을 반환해요. 패턴에 괄호가 없으면 성공 시 목록 (1)을 반환해요. 실패 시엔 괄호 유무와 무관하게 빈 목록이 반환돼요.
open(TTY, "+</dev/tty")
|| die "can't access /dev/tty: $!";
<TTY> =~ /^y/i && foo(); # do foo if desired
if (/Version: *([0-9.]*)/) { $version = $1; }
next if m#^/usr/spool/uucp#;
# poor man's grep
$arg = shift;
while (<>) {
print if /$arg/;
}
if (($F1, $F2, $Etc) = ($foo =~ /^(\S+)\s+(\S+)\s*(.*)/))
마지막 예시는 $foo를 처음 두 단어와 나머지 줄로 나눠 그 세 필드를 $F1, $F2, $Etc에 할당해요. 어떤 변수라도 할당됐으면(즉 패턴이 매치됐으면) 조건이 참이에요.
/g 수식어는 전역 패턴 매칭을 지정해요 — 문자열 안에서 가능한 한 많이 매치하는 거죠. 동작은 문맥에 따라 달라져요. 리스트 문맥에서는 정규식의 캡처링 괄호가 매치한 부분 문자열 목록을 반환해요. 괄호가 없으면 전체 패턴에 괄호가 있는 것처럼 매치된 모든 문자열 목록을 반환해요. 스칼라 문맥에서 m//g를 실행할 때마다 다음 매치를 찾아, 매치되면 참, 더 이상 매치가 없으면 거짓을 반환해요. 마지막 매치 후의 위치는 pos() 함수로 읽거나 설정할 수 있어요. 실패한 매치는 보통 검색 위치를 문자열 시작으로 리셋하지만, /c 수식어를 추가하면 피할 수 있어요.
\G 단언(assertion) — m//g 매치를 m/\G.../g와 섞을 수 있어요. \G는 이전 m//g(있었다면)가 멈춘 정확한 위치에 매치하는 0폭 단언이에요. /g 수식어 없이도 \G 단언은 연산 시작 시점의 pos()에 고정되지만, 매치는 당연히 한 번만 시도돼요.
# list context
($one,$five,$fifteen) = (`uptime` =~ /(\d+\.\d+)/g);
# scalar context
local $/ = "";
while ($paragraph = <>) {
while ($paragraph =~ /\p{Ll}['")]*[.!?]+['")]*\s/g) {
$sentences++;
}
}
say $sentences;
\G를 써서 lex처럼 스캐너를 만드는 유용한 관용구는 /\\G.../gc예요.
m?PATTERN?msixpodualngc — m/PATTERN/ 검색과 같지만 reset() 연산자 호출 사이에 한 번만 매치돼요. 파일 묶음에서 각 파일의 무언가의 첫 발생만 보고 싶을 때 유용한 최적화예요. match-once 동작은 매치 구분자가 ?라는 사실로 제어돼요; 다른 구분자로는 이게 정상 m// 연산자예요.
while (<>) {
if (m?^$?) {
# blank line between header and body
}
} continue {
reset if eof; # clear m?? status for next file
}
s/PATTERN/REPLACEMENT/msixpodualngcer — 문자열에서 패턴을 검색하고, 찾으면 그 패턴을 치환 텍스트로 바꿔 치환 횟수를 반환해요. 그 외엔 거짓(빈 문자열 ""이자 숫자 0인 값)을 반환해요. /r(비파괴) 옵션을 쓰면 문자열 복사본에서 치환을 실행하고, 치환 횟수 대신 치환이 일어났든 아니든 복사본을 반환해요. 원본 문자열은 /r을 쓸 때 절대 바뀌지 않아요. =~나 !~로 문자열을 지정하지 않으면 $_ 변수가 검색·수정돼요. 구분자로 단따옴표를 고르면 PATTERN이나 REPLACEMENT 어느 쪽에도 변수 보간이 안 돼요.
m//와 같은 옵션에 더해 치환 특유 옵션:
e Evaluate the right side as a block of code.
ee Evaluate the right side as a block of code, then eval()
the resulting string.
r Return substitution and leave the original string
untouched.
/e는 치환 부분을 본격적인 Perl 코드 블록으로 취급해 바로 그 자리에서 평가하게 해요. 두 번째 e 수식어는 코드 블록의 결과를 eval되게 해요. 즉 s/FOO/BAR/e는 매치 시점에 do { BAR }처럼 치환 문자열을 계산하고, s/FOO/BAR/ee는 eval(do { BAR })처럼 계산해요.
s/\bgreen\b/mauve/g; # don't change wintergreen
$path =~ s|/usr/bin|/usr/local/bin|;
s/Login: $foo/Login: $bar/; # run-time pattern
($foo = $bar) =~ s/this/that/; # copy first, then
# change
($foo = "$bar") =~ s/this/that/; # convert to string,
# copy, then change
$foo = $bar =~ s/this/that/r; # Same as above using /r
$foo = $bar =~ s/this/that/r
=~ s/that/the other/r; # Chained substitutes
# using /r
@foo = map { s/this/that/r } @bar # /r is very useful in
# maps
$count = ($paragraph =~ s/Mister\b/Mr./g); # get change-cnt
$_ = 'abc123xyz';
s/\d+/$&*2/e; # yields 'abc246xyz'
s/\d+/sprintf("%5d",$&)/e; # yields 'abc 246xyz'
s/\w/$& x 2/eg; # yields 'aabbcc 224466xxyyzz'
s/%(.)/$percent{$1}/g; # change percent escapes; no /e
s/%(.)/$percent{$1} || $&/ge; # expr now, so /e
s/^=(\w+)/pod($1)/ge; # use function call
$_ = 'abc123xyz';
$x = s/abc/def/r; # $x is 'def123xyz' and
# $_ remains 'abc123xyz'.
# expand variables in $_, but dynamics only, using
# symbolic dereferencing
s/\$(\w+)/${$1}/g;
# Add one to the value of any numbers in the string
s/(\d+)/1 + $1/eg;
# Titlecase words in the last 30 characters only (presuming
# that the substring doesn't start in the middle of a word)
substr($str, -30) =~ s/\b(\p{Alpha})(\p{Alpha}*)\b/\u$1\L$2/g;
# This will expand any embedded scalar variable
# (including lexicals) in $_ : First $1 is interpolated
# to the variable name, and then evaluated
s/(\$\w+)/$1/eeg;
# Same as above, but perhaps clearer
s/(\$\w+)/eval($1)/eg;
# Delete (most) C comments.
$program =~ s {
/\* # Match the opening delimiter.
.*? # Match a minimal number of characters.
\*/ # Match the closing delimiter.
} []gsx;
s/^\s*(.*?)\s*$/$1/; # trim whitespace in $_,
# expensively
for ($variable) { # trim whitespace in $variable,
# cheap
s/^\s+//;
s/\s+$//;
}
s/([^ ]*) *([^ ]*)/$2 $1/; # reverse 1st two fields
$foo !~ s/A/a/g; # Lowercase all A's in $foo; return
# 0 if any were found and changed;
# otherwise return 1
마지막 예시에서 \ 대신 $를 쓰는 점에 주목하세요. sed와 달리 우리는 <digit> 형태를 왼쪽에서만 써요. 다른 곳에선 $<digit>이에요.
때로 g만으로는 원하는 모든 변경을 얻지 못할 때가 있어요. 흔한 두 경우:
# put commas in the right places in an integer
1 while s/(\d)(\d\d\d)(?!\d)/$1,$2/g;
# expand tabs to 8-column spacing
1 while s/\t+/' ' x (length($&)*8 - length($`)%8)/e;
음역 따옴표류 연산자 (Transliteration Quote-Like Operators)
tr/SEARCHLIST/REPLACEMENTLIST/cdsr 또는 y/SEARCHLIST/REPLACEMENTLIST/cdsr — 검색 목록에서 찾은(또는 /c 수식어를 지정하면 찾지 못한) 문자의 모든 발생을 치환 목록의 위치상 대응 문자로 음역해요. 수식어에 따라 일부는 삭제할 수도 있어요. /r 플래그를 지정하지 않으면 치환되거나 삭제된 문자 수를 반환해요. =~나 !~로 문자열을 지정하지 않으면 $_ 문자열이 음역돼요. sed에 열광하는 사람들을 위해 y가 tr의 동의어로 제공돼요.
/r(비파괴) 옵션이 있으면 새 복사본을 만들어 그 문자들을 음역하고, 수정 여부와 무관하게 카운트 대신 그 복사본을 반환해요. 원본 문자열은 항상 그대로 남아요. /r을 쓰지 않으면 =~로 지정한 문자열은 스칼라 변수·배열 요소·해시 요소·그중 하나로의 할당, 즉 lvalue여야 해요.
SEARCHLIST와 REPLACEMENTLIST를 구분하는 문자는 슬래시뿐 아니라 인쇄 가능한 아무 문자나 될 수 있어요. 단따옴표(tr'SEARCHLIST'REPLACEMENTLIST')면 유일한 보간은 \\ 쌍에서 \를 제거하는 것이에요. 그 외에는 하이픈으로 문자 범위를 지정할 수 있어요. tr/A-J/0-9/는 tr/ACEGIBDFHJ/0246813579/와 같은 치환을 해요.
tr은 괄호 문자 클래스와 완전히 같진 않지만, 완전한 패턴보다는 그것에 (상당히) 더 가까워요. 예를 들어 어느 목록에든 한 번 이상 나타나는 문자는 패턴과 다르게 동작하고, tr 목록은 \d나 \pL 같은 백슬래시 문자 클래스나 변수 보간을 허용하지 않아요. 그래서 "$", "@"는 항상 리터럴로 취급돼요.
가장 범위가 이식 가능한 것들은 A-Z, a-z, 0-9 범위의 어떤 부분집합(리터럴 문자로 표현된)과, 한쪽 또는 양쪽 끝점이 \N{...}로 표현된 범위예요. 확실하지 않으면 문자 집합을 전부 풀어 쓰세요.
옵션:
c Complement the SEARCHLIST.
d Delete found but unreplaced characters.
r Return the modified string instead of a count, and leave the
original string untouched.
s Squash duplicate replaced characters.
/d 수식어를 지정하면 REPLACEMENTLIST에 없는 SEARCHLIST 지정 문자는 삭제돼요. /s 수식어를 지정하면 모두 같은 문자로 음역된 연속 문자 시퀀스가 그 문자 하나로 압축돼요.
my $x = "aaabbbca";
$x =~ tr/ab/dd/s; # $x now is "dcd"
/d 수식어를 쓰면 REPLACEMENTLIST는 항상 지정된 대로 정확히 해석돼요. 그 외에 REPLACEMENTLIST가 SEARCHLIST보다 짧으면, (있다면) 마지막 문자가 충분히 길어질 때까지 복제돼요. REPLACEMENTLIST가 비었을 때만 마지막 문자가 없는데, 그 경우 REPLACEMENTLIST는 SEARCHLIST에서 복사돼요. tr/abcd//는 tr/abcd/abcd/와 같고, tr/abcd//d는 s/[abcd]//g와 같아요.
/c 수식어를 지정하면 음역할 문자가 SEARCHLIST에 없는 것들, 즉 보수(complement)예요.
몇 가지 예시:
$ARGV[1] =~ tr/A-Z/a-z/; # canonicalize to lower case ASCII
$cnt = tr/*/*/; # count the stars in $_
$cnt = tr/*//; # same thing
$cnt = $sky =~ tr/*/*/; # count the stars in $sky
$cnt = $sky =~ tr/*//; # same thing
$cnt = $sky =~ tr/*//c; # count all the non-stars in $sky
$cnt = $sky =~ tr/*/*/c; # same, but transliterate each non-star
# into a star, leaving the already-stars
# alone. Afterwards, everything in
# $sky is a star.
$cnt = tr/0-9//; # count the ASCII digits in $_
tr/a-zA-Z//s; # bookkeeper -> bokeper
tr/o/o/s; # bookkeeper -> bokkeeper
tr/oe/oe/s; # bookkeeper -> bokkeper
tr/oe//s; # bookkeeper -> bokkeper
tr/oe/o/s; # bookkeeper -> bokkopor
($HOST = $host) =~ tr/a-z/A-Z/;
$HOST = $host =~ tr/a-z/A-Z/r; # same thing
$HOST = $host =~ tr/a-z/A-Z/r # chained with s///r
=~ s/:/ -p/r;
tr/a-zA-Z/ /cs; # change non-alphas to single space
@stripped = map tr/a-zA-Z/ /csr, @original;
# /r with map
tr [\200-\377]
[\000-\177]; # wickedly delete 8th bit
$foo !~ tr/A/a/ # transliterate all the A's in $foo to 'a',
# return 0 if any were found and changed.
# Otherwise return 1
음역된 문자는 더 이상의 음역을 위해 재검색되지 않아요. 문자가 여러 음역을 받으면 첫 번째 것만 사용돼요. tr/AAA/XYZ/는 어떤 A든 X로 음역해요. 음역 테이블이 컴파일 타임에 만들어지므로 SEARCHLIST나 REPLACEMENTLIST 모두 이중 따옴표 보간을 받지 않아요. 변수를 쓰고 싶다면 eval()을 써야 해요:
eval "tr/$oldlist/$newlist/";
die $@ if $@;
eval "tr/$oldlist/$newlist/, 1" or die $@;
Here-doc
<<EOT과 <<~EOT — 줄 지향 따옴표 형태로, 셸의 "here-document" 문법에 기반해요. << 뒤에 따옴표 처리된 자료를 끝낼 문자열을 지정하고, 현재 줄 다음부터 종결 문자열까지의 모든 줄이 그 항목의 값이 돼요.
my $endng = <<WHIMPER;
This is the way the text ends.
This is the way the text ends.
Not with a bang, but with a
WHIMPER
여기서 종결자는 식별자 "WHIMPER"예요. 대개 사람들이 식별자를 대문자로 해 눈에 띄게 하지만, 그것은 필요 없는 관례일 뿐이에요. 종결자는 따옴표로 감쌀 수 있고, 그렇게 안 하면 here-doc 텍스트는 이중 따옴표로 감싼 것처럼 정확히 동작해요.
my $person = 'John';
print uc <<EOT;
Hello, $person!
And the text goes on.
EOT
이렇게 됩니다: HELLO, JOHN! AND THE TEXT GOES ON. here-doc은 다른 것들과 섞을 수 있고, 여러 개를 가질 수 있어요:
print <<EOT, "Followed by the next argument\n";
Hello, $person!
And the text goes on.
EOT
종결자는 단일 단어일 필요 없고, 따옴표로 감싼 텍스트일 수도 있어요:
my $pagliaci = << "La Commedia e finita!";
A troupe comes to town to perform a play, a comedy. The lead actress
and lead actor are in an unhappy marriage. On stage, he stabs her
for real; then he stabs her lover who has rushed from the audience to
defend her. Both die.
La Commedia e finita!
종결자가 따옴표로 감싸이지 않으면 <<와 식별자 사이에 공백이 있으면 안 돼요. 종결자에 대한 따옴표 규칙은 Perl의 따옴표 규칙과 무관해요. 그것을 감싸는 데는 "", '', ``만 쓸 수 있고, q(), qq() 같은 건 못 써요. 종결 문자열은 종결 줄에 (따옴표 없이, 주변 공백 없이) 홀로 나타나야 해요. 문장을 끝내려면 세미콜론을 붙여야 한다는 걸 잊지 마세요.
마지막 줄에 줄 종결자를 없애려면 chomp()를 써요:
chomp($string = <<'END');
This is the first line.
This second line won't end in a \n.
END
Perl v5.26부터 물결표 ~ 수식어로 here-doc을 들여쓰기 할 수 있어요:
if ($some_var) {
print <<~EOF;
This is a here-doc
EOF
}
here-doc 끝을 표시하는 구분자를 담은 줄이 전체의 들여쓰기 템플릿을 결정해요. here-doc 안의 어떤 비어 있지 않은 줄이 종결 줄의 정확한 들여쓰기로 시작하지 않으면 컴파일이 실패해요. 종결자 앞을 넘는 추가 앞 공백은 보존돼요.
구분자 인용(Quoting the delimiter) — 앞서 언급했듯 종결 문자열은 따옴표로 감쌀 수 있어요. 세 종류가 가능해요.
- 이중 따옴표(Double Quotes) — 종결 단어·문자열을 감싸는 이중 따옴표는 아무 따옴표도 없는 것처럼 동작해요. 즉 텍스트가 일반 이중 따옴표 문자열과 정확히 같은 규칙으로 보간돼요.
- 단일 따옴표(Single Quotes) — 단일 따옴표를 쓰면 텍스트가 리터럴로 취급되고 내용이 보간되지 않아요. 단일 따옴표 here-doc와 단일 따옴표 문자열의 차이는,
\\가 다른 모든 따옴표 구조에서처럼 하나가 아니라 두 개의 백슬래시로 취급된다는 점이에요. 셸에서처럼<<뒤의 백슬래시 bareword는 단일 따옴표 문자열과 같은 뜻이에요:<<\VISTA는<<'VISTA'와 같은 거예요. 이 두 형태는 Perl에서 내용 escaping을 걱정할 필요가 없는 유일한 두 가지 따옴표 방식이에요. - 백틱(Backticks) — 종결 문자열을 감싸는 데 백틱을 쓰면 here doc 내용이 백틱에 임베디드된 문자열처럼 취급돼요. 즉 이중 따옴표처럼 보간된 후 셸을 통해 실행되고, 실행 결과가 반환돼요.
따옴표 구조 파싱의 상세 (Gory details of parsing quoted constructs)
여러 해석이 가능한 것을 만날 때 Perl은 DWIM("Do What I Mean") 원칙으로 가장 그럴듯한 해석을 골라요. 이 섹션은 Perl이 따옴표 구조를 어떻게 다루는지 명확히 하려는 목적이에요.
가장 중요한 Perl 파싱 규칙은 첫 번째 것이에요: 따옴표 구조를 처리할 때 Perl은 먼저 그 구조의 끝을 찾은 다음 내용을 해석해요. 이 규칙을 이해하면 나머지 섹션은 첫 읽기에서 건너뛸 수 있어요.
- 끝 찾기(Finding the end) — 첫 패스는 따옴표 구조의 끝을 찾는 거예요. 구조가 here-doc이면 끝 구분자는 내용으로 종결 문자열을 가진 줄이에요. here-doc이 아닌 구조는 단일 문자가 시작·끝 구분자로 쓰여요. 시작 구분자가 여는 구두점(
(,[,{,<)이면 끝 구분자는 대응하는 닫는 구두점(),],},>)이에요. 시작 구분자가/같은 짝 없는 문자나 닫는 구두점이면 끝 구분자는 시작 구분자와 같아요. 단일 문자 구분자를 찾을 때는 이스케이프된 구분자와\\가 건너뛰어져요. 괄호 구분자면 중첩 쌍도 건너뛰어져요. 세 부분 구분자 구조(s///,y///,tr///)라면 검색이 한 번 더 반복돼요. - 보간(Interpolation) — 다음 단계는 얻은 텍스트(이제 구분자 독립적)를 보간하는 거예요. 여러 경우가 있어요.
<<<'EOF'는 보간이 수행되지 않아요.m''는 이 단계에서 보간이 없어요.'',q//,tr'''는\\쌍에서\를 제거하는 것이 유일한 보간이에요.tr///,y///는 변수 보간이 없어요."",``,qq//,qx//,<file*glob>,<<"EOF"는\Q,\U,\u,\L,\l,\F(가능하면\E와 짝지어)가 대응하는 Perl 구조로 변환되고, 다른 이스케이프 시퀀스가 적절한 전개로 바뀌어요.\Q와\E사이에 떨어지는 무엇이든 일반적인 방식으로 보간된다는 점을 강조할게요. 보간된 스칼라·배열은 내부적으로join과"."연결 연산으로 변환돼요. - 정규식 파싱(Parsing regular expressions) — 이전 단계들은 Perl 코드 컴파일 동안 수행됐지만, 이 단계는 런타임에 일어나요. 전처리 후 결과 문자열이 RE 엔진에 컴파일을 위해 전달돼요. RE 엔진은 문자열을 왼쪽에서 오른쪽으로 스캔해 유한 자동장치(finite automaton)로 변환해요. 백슬래시 문자는 대응하는 리터럴 문자열로 바뀌거나(
\{처럼), 유한 자동장치에 특수 노드(\b처럼)를 생성해요. /x가 있으면 공백과#-스타일 주석은 무시돼요. - 정규식 최적화(Optimization of regular expressions) — 이 단계는 완전성을 위해 나열된 거예요. 의미를 바꾸지 않으므로 세부사항은 문서화되지 않았어요. 이 단계에서
split()은^/를 조용히^/m으로 최적화해요.
I/O 연산자 (I/O Operators)
알아 두어야 할 I/O 연산자가 몇 가지 있어요.
백틱(grave accents)으로 감싼 문자열은 먼저 이중 따옴표 보간을 겪어요. 그런 다음 외부 명령으로 해석되고, 그 명령의 출력이 백틱 문자열의 값이에요(셸에서처럼). 스칼라 문맥에서는 모든 출력으로 구성된 단일 문자열이 반환돼요. 리스트 문맥에서는 출력 줄당 하나씩 값 목록이 반환돼요. 명령의 상태 값은 $?에 반환돼요. csh와 달리 반환 데이터에 번역이 없어요 — newline은 newline으로 남아요. 어떤 셸과도 달리 단따옴표는 명령의 변수 이름을 해석에서 숨기지 않아요. 백틱의 일반화된 형태는 qx//이거나 readpipe 함수를 호출하면 돼요.
스칼라 문맥에서 각괄호(angle brackets)의 파일핸들을 평가하면 그 파일에서 다음 줄을 얻어요(있으면 newline 포함), 파일 끝이나 오류에선 undef를 반환해요. 보통 반환값을 변수에 할당해야 하지만, 자동 할당이 일어나는 한 가지 상황이 있어요. 입력 심볼이 while 문의 조건 안의 유일한 것이면(포장된 for(;;) 루프여도), 그 값이 자동으로 전역 변수 $_에 할당돼요. 또한 입력 심볼이나 스칼라로의 입력 심볼 명시적 할당이 while/for 조건으로 쓰이면, 조건은 실제로 그 표현식 값의 **정의됨(definedness)**을 검사하지 일반 진리값을 검사하지 않아요.
그래서 다음 줄들은 동등해요:
while (defined($_ = <STDIN>)) { print; }
while ($_ = <STDIN>) { print; }
while (<STDIN>) { print; }
for (;<STDIN>;) { print; }
print while defined($_ = <STDIN>);
print while ($_ = <STDIN>);
print while <STDIN>;
어휘 변수에 할당하는 비슷한 예:
while (my $line = <STDIN>) { print $line }
이 루프 구조에서 할당된 값(자동이든 명시적이든)은 정의됐는지 검사돼요. 정의 검사는 ""나 끝 newline 없는 "0"처럼 Perl이 거짓으로 취급할 문자열 값을 가진 줄에서 생기는 문제를 피해요. 정말 그런 값으로 루프를 끝내고 싶다면 명시적으로 검사해야 해요:
while (($_ = <STDIN>) ne '0') { ... }
while (<STDIN>) { last unless $_; ... }
STDIN, STDOUT, STDERR 파일핸들은 미리 정의돼 있어요. /perlfunc/open 등을 포함해 추가 파일핸들을 open() 함수로 만들 수 있어요.
<FILEHANDLE>이 목록을 원하는 문맥에 쓰이면 모든 입력 줄로 구성된 목록이 반환돼요. <FILEHANDLE>은 readline(*FILEHANDLE)으로도 쓸 수 있어요.
null 파일핸들 <>(때로 diamond 연산자)은 특별해요: sed와 awk, 그리고 파일 이름 목록을 받아 입력의 각 줄에 같은 일을 하는 다른 유닉스 필터 프로그램의 동작을 흉내 내는 데 쓸 수 있어요. <>의 입력은 표준 입력이거나 명령줄에 나열된 각 파일에서 와요. 이렇게 동작해요: <>을 처음 평가할 때 @ARGV 배열을 검사하고, 비어 있으면 $ARGV[0]을 "-"로 설정해요(열면 표준 입력). @ARGV 배열은 그다음 파일 이름 목록으로 처리돼요. 루프
while (<>) {
... # code for each line
}
는 다음 Perl 같은 의사 코드와 동등해요:
unshift(@ARGV, '-') unless @ARGV;
while ($ARGV = shift) {
open(ARGV, $ARGV);
while (<ARGV>) {
... # code for each line
}
}
실제로 @ARGV 배열을 시프트하고 현재 파일 이름을 $ARGV 변수에 넣어요. 내부적으로 ARGV 파일핸들을 쓰고, <>는 마법적인 <ARGV>의 동의어일 뿐이에요. null 파일핸들은 open의 두 인자 형태를 쓰므로 특수 문자를 해석해요. @ARGV의 모든 항목을 파일 이름으로 해석하길 원하면 ARGV::readonly 모듈을 쓰거나 이중 다이아몬드 연산자를 써요:
while (<<>>) {
print;
}
while 안에서 이중 각괄호를 쓰면 open이 세 인자 형태(둘째 인자가 <)를 쓰게 해서 ARGV의 모든 인자를 리터럴 파일 이름(포함 "-")으로 취급해요. @ARGV를 원하는 파일 목록으로 설정할 수도 있어요:
@ARGV = grep { -f && -T } glob('*') unless @ARGV;
파이프 명령으로 설정할 수도 있어요:
@ARGV = map { /\.(gz|Z)$/ ? "gzip -dc < $_ |" : $_ } @ARGV;
각괄호 안이 단순 스칼라 변수(예: $foo)면 그 변수가 입력받을 파일핸들의 이름, 그 typeglob, 또는 그것에 대한 참조를 담고 있어요:
$fh = \*STDIN;
$line = <$fh>;
각괄호 안이 파일핸들도, 파일핸들 이름·typeglob·typeglob 참조를 담은 단순 스칼라 변수도 아니면, glob할 파일 이름 패턴으로 해석돼요. 목록 문맥에 따라 파일 이름 목록이나 목록의 다음 파일 이름이 반환돼요. <*.c> 같은 예:
while (<*.c>) {
chmod 0644, $_;
}
는 대략:
open(FOO, "echo *.c | tr -s ' \t\r\f' '\\012\\012\\012\\012'|");
while (<FOO>) {
chomp;
chmod 0644, $_;
}
와 동등해요(glob는 내부적으로 표준 File::Glob 확장으로 수행된다는 것만 빼고요). 물론 가장 짧은 방법은:
chmod 0644, <*.c>;
(파일)glob은 새 목록을 시작할 때만 (임베디드) 인자를 평가해요. 변수 보간을 하려 한다면 glob() 함수를 쓰는 게 훨씬 낫습니다. 각괄호 기반 glob 표현식이 while이나 for 루프의 조건으로 쓰이면 $_에 암묵적으로 할당돼요.
상수 폴딩 (Constant Folding)
C처럼 Perl은 연산자에 대한 모든 인자가 정적이고 부수 효과가 없을 때마다 컴파일 타임에 어느 정도의 표현식 평가를 해요. 특히 문자열 연결은 변수 치환을 하지 않는 리터럴 사이에서 컴파일 타임에 일어나요. 백슬래시 보간도 컴파일 타임에 일어나요.
No-op
Perl은 공식적으로 no-op 연산자가 없지만, 벌거벗은 상수 0과 1은 void 문맥에서 경고를 만들지 않도록 특별 취급돼요. 그래서 1 while foo();를 안전하게 할 수 있어요.
비트 문자열 연산자 (Bitwise String Operators)
어떤 크기의 비트 문자열이든 비트 연산자(~ | & ^)로 조작할 수 있어요. 이진 비트 연산의 피연산자가 다른 크기의 문자열이면, |와 ^는 더 짧은 피연산자에 오른쪽에 추가 0 비트가 있는 것처럼 동작하고, &는 더 긴 피연산자가 더 짧은 것의 길이로 잘린 것처럼 동작해요.
# ASCII-based examples
print "j p \n" ^ " a h"; # prints "JAPH\n"
print "JA" | " ph\n"; # prints "japh\n"
print "japh\nJunk" & '_____'; # prints "JAPH\n";
print 'p N$' ^ " E<H\n"; # prints "Perl\n";
비트 문자열을 조작하려 한다면 확실히 비트 문자열을 공급해야 해요: 피연산자가 숫자이면 숫자 비트 연산을 암시해요. ""이나 0+로 의도한 연산 타입을 명시할 수 있어요:
$foo = 150 | 105; # yields 255 (0x96 | 0x69 is 0xFF)
$foo = '150' | 105; # yields 255
$foo = 150 | '105'; # yields 255
$foo = '150' | '105'; # yields string '155' (under ASCII)
$baz = 0+$foo & 0+$bar; # both ops explicitly numeric
$biz = "$foo" ^ "$bar"; # both ops explicitly stringy
이런 다소 예측 불가능한 동작은 Perl 5.22의 새 기능인 "bitwise"로 피할 수 있어요. use feature 'bitwise' 또는 use v5.28로 켜요. 이 기능 아래에서 네 표준 비트 연산자(~ | & ^)는 항상 숫자예요. 각 연산자 뒤에 점을 붙이면(~. |. &. ^.) 피연산자를 문자열로 취급하도록 강제해요. 피연산자에 0xFF를 넘는 서수 값을 가진 문자(즉 UTF-8로만 표현 가능)가 있으면 치명적 오류예요.
정수 산술 (Integer Arithmetic)
기본적으로 Perl은 대부분의 산술을 부동소수점에서 해야 한다고 가정해요. 하지만 use integer;라고 하면 감싸는 BLOCK 끝까지 정수 연산을 쓰도록 컴파일러에 지시할 수 있어요. 안쪽 BLOCK이 no integer;로 취소할 수 있어요. 다만 이게 모든 게 정수라는 뜻은 아니고, Perl이 산술·비교·비트 연산자에 정수 연산을 쓸 것이라는 뜻일 뿐이에요. 예를 들어 use integer 아래서도 sqrt(2)를 취하면 여전히 1.4142135623731 정도를 얻어요. 숫자에 쓰인 비트 연산자(&, |, ^, ~, <<, >>)는 항상 정수 결과를 만들어요. 기본적으로 그 결과는 부호 없는 정수로 해석되지만, use integer가 효과 중이면 부호 있는 정수로 해석돼요. 예를 들어 보통 ~0은 큰 정수 값으로 평가되지만, use integer; ~0은 2의 보수 머신에서 -1이에요.
부동소수점 산술 (Floating-point Arithmetic)
use integer가 정수 전용 산술을 제공하는 동안, 특정 소수 자리로 자동 반올림·절사를 제공하는 유사한 메커니즘은 없어요. 특정 자릿수로 반올림하려면 sprintf()나 printf()가 보통 가장 쉬운 경로예요. 부동소수점 숫자는 수학자가 실수라고 부르는 것의 근사일 뿐이에요:
printf "%.20g\n", 123456789123456789;
# produces 123456789123456784
정확한 부동소수점 동일성·비동일성 검사는 좋은 생각이 아니에요. 두 부동소수점이 특정 소수 자리까지 같은지 비교하는 (비교적 비싼) 우회로:
sub fp_equal {
my ($X, $Y, $POINTS) = @_;
my ($tX, $tY);
$tX = sprintf("%.${POINTS}g", $X);
$tY = sprintf("%.${POINTS}g", $Y);
return $tX eq $tY;
}
POSIX 모듈(표준 perl 배포의 일부)이 ceil(), floor(), 기타 수학·삼각 함수를 구현해요. Math::Complex 모듈은 실수와 허수 모두에 동작하는 수학 함수를 정의해요. 금융 응용에서의 반올림은 심각한 영향을 줄 수 있어서, 사용하는 반올림 방법을 정밀하게 명시해야 해요.
더 큰 숫자 (Bigger Numbers)
표준 Math::BigInt, Math::BigRat, Math::BigFloat 모듈과 bignum, bigint, bigrat 프래그마는 가변 정밀도 산술과 오버로드된 연산자를 제공해요, 현재는 꽤 느리지만요. 제한된 정밀도 표현과 관련된 일반적인 함정을, 공간과 상당한 속도 대가로 피해요:
use 5.010;
use bigint; # easy interface to Math::BigInt
$x = 123456789123456789;
say $x * $x;
+15241578780673678515622620750190521
유리수로는:
use 5.010;
use bigrat;
$x = 3/22;
$y = 4/6;
say "x/y is ", $x/$y;
say "x*y is ", $x*$y;
x/y is 9/44
x*y is 1/11
과거 섹션들
C-style Logical Or, C-style Logical Xor, Logical Defined-Or 섹션은 하나의 C-style Logical Or, Xor, and Defined Or 섹션으로 대체됐어요 (머릿글은 기존 링크를 깨지 않기 위해 유지됨). Quote-Like Operators는 Simpler Quote-Like Operators로, Indented Here-docs는 Here-docs로 대체·합병됐어요.
부록: 추가 쌍 구분자 목록 (APPENDIX)
extra_paired_delimiters 기능이 켜지면 Perl이 추가로 인식하는 유니코드 쌍 구분자 전체 목록은 Unicode 16.0 기준으로 이 페이지 원본의 APPENDIX에 수백 줄의 코드 포인트 표로 수록돼 있어요. 일반적인 ASCII 괄호((), <>, [], {})부터 각종 화살표·수학 기호·따옴표 문자의 좌우 쌍까지를 담고 있으며, 실무에서는 이 표를 셸에서 perldoc perlop로 직접 확인하는 게 가장 정확해요.
더 알아보기
perlsyn— 문법과 문장perlre— 정규식perlfunc— 내장 함수perlref— 참조integer,bigint,bignum,bigrat— 산술 프래그마