이번 펄 튜토리얼에서는 펄에서의 문맥 민감성에 대해서 살펴보겠습니다.

대부분의 다른 언어와 마찬가지로 영어도 단어는 여러가지 뜻을 가질수 있습니다. 예를들면 "left"는 여러가지의 뜻을 가집니다.

I left the building. (나는 빌딩을 떠났습니다.)

I turned left at the building. (나는 빌딩에서 왼쪽으로 돌았습니다.)

우리는 단어 주변의 문장들을 통해 정확한 의미를 이해합니다. 이것을 문맥이라 부릅니다.

Perl 5도 위와 유사합니다. 단어와 함수 호출, 다른 표현식이 문맥에 따라 다른 뜻을 가질 수 있습니다. 이런 연유로 배우기는 조금 어렵지만 덕분에 표현력은 더 풍부해집니다.

펄에는 두 가지 중요한 문맥인 스칼라 문맥과 목록 문맥이 있습니다.

목록 문맥에서의 배열

다음 예제를 살펴 보겠습니다.

my @words = ('Foo', 'Bar', 'Baz');
my @names = @words;

앞 예제의 할당문 이후에 @names에는 @words 안에 있던 값들이 복사됩니다.

다른 배열로 배열을 할당하면 배열의 내용을 복사합니다.

스칼라 문맥에서의 배열

my @words = ('Foo', 'Bar', 'Baz');
my $people =  @words;

이번에는 $people 스칼라 변수에 @words 배열을 할당 했습니다.

다른 언어들은 다르게 동작하겠지만 펄은 이 할당의 경우 배열의 원소의 개수를 스칼라 변수에 할당합니다.

이 동작은 임의적이며 앞의 예제에서는 그다지 유용하지는 않습니다. 하지만 이 동작이 유용한 경우도 많습니다.

스칼라 문맥과 목록 문맥

앞의 두 경우를 스칼라 문맥과 목록 문맥이라고 부릅니다. 이것은 하나의 값을 반환 받기를 기대(스칼라 문맥)하거나 여러 개의 값을 반환받기를 기대(목록 문맥)하는 것입니다. 목록 문맥에서 값의 개수는 0 또는 1, 2개는 물론 어떤 숫자라도 될 수 있습니다.

if 문의 문맥

다음 예제를 살펴봅시다.

my @words = ('Foo', 'Bar', 'Baz');

if (@words) {
   say "There are some words in the array";
}

if 문의 조건 영역 안쪽에서는 정확히 하나의 값을 기대합니다. 그렇다면 반드시 스칼라 문맥이어야 합니다.

이제 우리는 스칼라 문맥에서 배열의 값은 배열 내 원소의 개수라는 것을 알고 있습니다. 또한 배열이 비어있다면 이 값은 0(거짓)이며, 배열이 1개 또는 그 이상의 요소를 가지고 있을 경우 다른 양수 값()이란 것 역시 알고 있습니다.

따라서 앞의 예제에서 판단하는 if (@words) 코드는 배열에 어떤 요소가 있는지 확인을 하며 해당 배열이 비어있다면 거짓입니다.

if 문 주위를 if (! @words)로 바꾼다면 배열이 비어있을 때 참이됩니다.

스칼라 그리고 목록 문맥

이전 기사에서 localtime() 함수가 스칼라 문맥과 목록 문맥에서 어떻게 동작하는지 살펴보았고, 조금 전에는 배열이 스칼라 문맥과 목록 문맥에서 어떻게 동작하는지 살펴보았습니다.

문맥과 관련해서는 일반적인 규칙이 있지는 않습니다. 여러분은 각각의 경우에 대해 배워야하지만 그래도 꽤 명백합니다. 어쨌든 perldoc으로 함수를 찾으면 각각의 함수에서 문맥에 대한 설명을 볼 수 있습니다. 적어도 이 경우는 스칼라 문맥과 목록 문맥이 서로 다른 결과를 반환하는 경우입니다.

이제 우리는 펄에서의 몇 가지 표현식 예제를 더 살펴보고 그 예제들이 만들어내는 문맥의 종류가 무엇인지 살펴볼 것입니다.

스칼라 문맥 만들기

스칼라 변수에는 무엇을 할당하든 스칼라 문맥이 됨을 이미 살펴보았습니다. 이것을 설명하면 다음과 같습니다.

$x = SCALAR;

배열 내 각각의 원소 역시 스칼라이므로 각각의 원소에 할당하는 것 역시 스칼라 문맥입니다.

$word[3] = SCALAR;

문자열 결합 연산자는 연산자 좌우 모두 문자열을 기대하기 때문에 양쪽 모두 스칼라 문맥입니다.

"string" . SCALAR;

연산자의 오른쪽이 스칼라 문맥일 뿐만 아니라 왼쪽 역시 스칼라 문맥입니다.

SCALAR . "string"

동일한 관점에서 다음 예제를 살펴볼까요.

my @words = ('Foo', 'Bar', 'Baz');
say "Number of elements: " . @words;
say "It is now " . localtime();

앞의 예제는 다음 결과를 출력합니다.

Number of elements: 3
It is now Thu Feb 30 14:15:53 1998

수치 연산자는 일반적으로 양쪽에 두 개의 숫자, 즉 두 개의 스칼라를 기대합니다. 그러므로 수치 연산자는 양쪽 모두 스칼라 문맥입니다.

5 + SCALAR;

SCALAR + 5;

목록 문맥 만들기

목록 문맥을 만드는 구문을 살펴보겠습니다.

그 중 하나는 배열에 할당하는 것입니다.

@x = LIST;

목록에 할당하는 것 역시 목록 문맥입니다.

($x, $y) = LIST;

심지어 목록이 요소를 하나만 가지고 있다하더라도 목록 문맥입니다.

($x) =  LIST;

앞의 예제의 경우 사람들이 쉽게 속아 넘어가므로 중요합니다.

괄호가 중요한 경우는 언제인가요?

use strict;
use warnings;
use 5.010;

my @words = ('Foo', 'Bar', 'Baz');

my ($x) = @words;
my $y   = @words;

say $x;
say $y;

결과는 다음과 같습니다.

Foo
3

앞의 예제는 괄호가 매우 중요한 몇 안되는 경우 중 하나 입니다.

첫 번째 할당문인 my ($x) = @words;에서는 스칼라 변수(들)로 구성된 목록에 값을 할당합니다. 이로인해 할당의 오른쪽은 목록 문맥이 됩니다. 즉, 왼쪽의 목록에 배열의 을 복사한다는 의미입니다. 왼쪽에는 하나의 스칼라만 있기 때문에 배열의 첫번째 항목만 복사하고 나머지는 복사하지 않습니다.

두 번째 할당문인 my $y = @words;에서는 스칼라 변수에 직접 값을 할당합니다. 이로인해 할당의 오른쪽은 스칼라 문맥이 됩니다. 스칼라 문맥에서 배열은 배열내 요소의 개수를 반환합니다.

이것은 함수에 매개변수 전달하기를 살펴볼떄 매우 중요합니다.

스칼라 문맥을 강제하기

print()say() 함수는 매개변수로서 목록 문맥을 받아들입니다. 그렇다면 배열의 개수를 출력하고 싶을때 어떻게 해야할까요? 또는 localtime() 함수가 반환하는 미려한 날짜 형식을 그대로 출력하려면 어떻게 해야할까요?

다음 예제를 살펴보세요.

use strict;
use warnings;
use 5.010;

my @words = ('Foo', 'Bar', 'Baz');

say @words;
say localtime();

결과는 다음과 같습니다.

FooBarBaz
3542071011113100

첫 번째 출력 결과는 어떻게든 이해는 할 수 있습니다. 이 값은 배열의 항목이 하나로 합쳐진 결과입니다. 두 번째 출력 결과는 혼란스럽습니다.

여러분이 생각하는 time() 함수의 결과와는 같지 않습니다. 사실 이 값은 목록 문맥에서 localtime() 함수가 반환하는 9개의 숫자입니다. 기억이 나지않는다면 19100년 기사를 살펴보세요.

해결 방법은 scalar() 함수를 사용하는 것입니다. scalar() 함수는 입력 받은 매개 변수를 스칼라 문맥으로 만듭니다. 사실 이것이 scalar() 함수가 하는 전부입니다. 어떤 사람들은 이것을 복수를 단수로 캐스팅하는 것이라고 생각하지만 캐스팅이란 단어는 펄 세계에서는 자주 사용되지 않는 것 같습니다.

say scalar @words;
say scalar localtime();

결과는 다음과 같습니다.

3
Mon Nov  7 21:02:41 2011

펄에서 배열의 길이 또는 크기

간단히 요약해서 펄에서 배열의 크기를 구하려면 scalar() 함수를 사용해서 배열을 강제로 스칼라 문맥으로 만들어 배열의 크기를 반환하게 하면됩니다.

기교 부리기

때로는 다음과 갈은 코드를 보게 되기도 합니다.

0 + @words;

이것은 기본적으로 배열의 크기를 구할때 사용하는 기교입니다. + 연산자는 좌우 양쪽에 스칼라 문맥을 만듭니다. 배열은 스칼라 문맥에서 배열의 크기를 반환할 것입니다. 여기에 0을 추가하기 때문에 결과 값은 변하지 않고 앞의 표현식에서는 배열의 크기를 반환합니다.

개인적으로는 scalar 함수를 사용해서 약간은 더 길지만 조금 더 명확하게 작성하는 것을 권합니다.