펄 언어 구조

펄 프로그래밍 언어의 구조는 언어의 문법 규칙과 프로그램이 구성되는 일반적인 방식을 모두 포함한다. 펄의 설계 철학은 일반적으로 인용되는 "어떤 일을 하는 데에는 하나 이상의 길이 있다"라는 표어에 표현되어 있다. 다중 패러다임, 동적 형식 언어로서 펄은 프로그램 설계에 상당한 유연성을 허용한다. 펄은 또한 모듈화를 장려한다. 이는 유닉스 기반의 구성 요소 기반 설계 구조에 기인하며, 100,000개 이상의 모듈로 구성된 커뮤니티 유지 관리 저장소인 CPAN 아카이브의 규모에 책임이 있다.

 기본 문법 
펄에서 최소한의 ""Hello, World!" 프로그램"은 다음과 같이 작성할 수 있다.
print "Hello, World!\n"
이것은 출력한다 문자열 Hello, World! 와 새 줄, 기호적으로 n 문자로 표현되며, 이 문자의 해석은 선행하는 이스케이프 문자(역슬래시)에 의해 변경된다. 버전 5.10부터 새로운 'say' 내장 함수는 훨씬 더 간단하게 동일한 효과를 낸다.
say "Hello, World!"

전체 펄 프로그램은 펄에 대한 명령줄 매개변수로도 지정될 수 있으므로, 동일한 프로그램은 명령줄에서도 실행될 수 있다 (유닉스의 예시):
$ perl -e 'print "Hello, World!\n"'

이 프로그램의 정식 형태는 약간 더 길다:

#!/usr/bin/env perl
print "Hello, World!\n";

해시 마크 문자는 펄에서 주석을 나타내며, 코드 줄의 끝까지 이어지고 컴파일러에 의해 무시된다 (Windows 제외). 여기에 사용된 주석은 특별한 종류이다: 이것은 셔뱅 줄이라고 불린다. 이는 유닉스 계열 운영 체제에 펄 인터프리터를 찾도록 지시하여, perl을 명시적으로 언급하지 않고도 프로그램을 호출할 수 있게 한다. (마이크로소프트 윈도우 시스템에서 펄 프로그램은 일반적으로 .pl 확장자를 펄 인터프리터와 연결하여 호출된다는 점에 유의하라. 이러한 상황을 처리하기 위해 perl은 셔뱅 줄을 감지하고 스위치를 위해 파싱한다.)

정식 형태의 두 번째 줄에는 펄에서 문장을 구분하는 데 사용되는 세미콜론이 포함되어 있다. 블록이나 파일에 단일 문장만 있는 경우 구분 기호는 불필요하므로 프로그램의 최소 형태에서 또는 일반적으로 블록이나 파일의 마지막 문장에서 생략할 수 있다. 정식 형태에는 포함되어 있는데, 이는 편집을 더 쉽게 만들기 위해 불필요할 때도 모든 문장을 종료하는 것이 일반적이기 때문이다: 세미콜론을 조정할 필요 없이 블록이나 파일의 끝에 코드를 추가하거나 이동할 수 있다.

펄 버전 5.10은 출력에 새 줄 문자를 암묵적으로 추가하는 say 함수를 도입하여, 최소한의 "Hello World" 프로그램을 더욱 짧게 만든다.

use 5.010; # 새로운 5.10 함수를 가져오려면 반드시 있어야 하며, 5.10이 아닌 5.010에 유의
say 'Hello, World!'

자료형

펄에는 여러 가지 기본적인 자료형이 있다. 가장 일반적으로 사용되고 논의되는 것은 스칼라, 배열, 해시, 파일 핸들, 그리고 서브루틴이다.

스칼라 값

문자열 값(리터럴)은 따옴표로 묶어야 한다. 문자열을 이중 따옴표로 묶으면 문자열에 나타나는 변수 이름의 값이 문자열에서 변수 이름을 자동으로 대체(또는 삽입됨)할 수 있다. 문자열을 작은 따옴표로 묶으면 변수 삽입이 방지된다.

예를 들어, $name이 "Jim"인 경우:

 그러면 print("My name is $name")은 "My name is Jim"을 출력한다 (이중 따옴표 내 삽입).
 하지만 print('My name is $name')은 "My name is $name"을 출력한다 (작은 따옴표 내 삽입 없음).

문자열에 큰따옴표를 포함시키려면 앞에 백슬래시를 붙이거나 문자열을 작은따옴표로 묶어야 한다. 작은따옴표를 포함시키려면 앞에 백슬래시를 붙이거나 문자열을 큰따옴표로 묶어야 한다.

문자열은 q 및 qq 따옴표와 유사한 연산자를 사용하여 따옴표로 묶을 수도 있다.
 'this'와 q(this)는 동일하다.
 "$this"와 qq($this)는 동일하다.

마지막으로, 여러 줄 문자열은 히어 도큐먼트를 사용하여 정의할 수 있다.

$multilined_string = <
 연산자(때로는 fat comma라고 불림)로 구분되면 따옴표 없이(barewords) 사용할 수 있다. 다음 줄은 동일하다.

%favorite = ('joe', "red", 'sam', "blue");
%favorite = (joe => 'red', sam => 'blue');

해시의 개별 값은 해당 키를 중괄호 안에 제공하여 접근한다. $ 시길은 접근된 요소를 스칼라로 식별한다. 예를 들어, 는 와 같다. 해시는 개별적으로 값을 설정하여 초기화할 수도 있다.

$favorite{joe} = 'red';
$favorite{sam} = 'blue';
$favorite{oscar} = 'green';

대신 @ 시길을 사용하여 여러 요소에 접근할 수도 있다(결과를 목록으로 식별). 예를 들어,
은 와 같다.

파일 핸들

파일 핸들은 리소스에 대한 읽기 및 쓰기 접근을 제공한다. 이것들은 대부분 디스크의 파일이지만, 장치, 파이프, 또는 심지어 스칼라 값일 수도 있다.

원래 파일 핸들은 패키지 변수로만 생성할 수 있었고, 다른 변수들과 구별하기 위해 ALL_CAPS 규칙을 사용했다. Perl 5.6 이상은 명명된 파일 핸들 대신 스칼라 변수도 허용하며, 이 변수는 익명 파일 핸들에 대한 참조로 설정된다(자동 생성).

타입글로브 값

타입글로브 값은 심볼 테이블 항목이다. 타입글로브의 주요 용도는 심볼 테이블 별칭을 만드는 것이다. 예를 들어:

*PI = \3.141592653; # 상수 스칼라 $PI 생성
*this = *that; # 모든 데이터 타입 'this'를 모든 데이터 타입 'that'의 별칭으로 생성

배열 함수

배열의 요소 수는 스칼라 컨텍스트에서 배열을 평가하거나 $# 시길을 사용하여 결정할 수 있다. 후자는 배열의 마지막 요소 인덱스를 제공하며, 요소의 수가 아니다. scalar()와 ($#array + 1) 표현식은 동일하다.

해시 함수

전체 해시에서 작동하는 몇 가지 함수가 있다. keys 함수는 해시를 받아 키 목록을 반환한다. 마찬가지로 values 함수는 해시의 값을 반환한다. 키와 값은 일관되지만 임의의 순서로 반환된다는 점에 유의한다.

# each을 호출할 때마다 다음 키/값 쌍이 반환된다.
# 모든 값이 결국 반환되지만 그 순서는
# 예측할 수 없다.
while (($name, $address) = each %addressbook) {
 print "$name lives at $address\n";
}

# 위와 유사하지만 알파벳순으로 정렬됨
foreach my $next_name (sort keys %addressbook) {
 print "$next_name lives at $addressbook{$next_name}\n";
}

제어 구조

펄에는 여러 종류의 제어 구조가 있다.

C, 자바스크립트, 자바 프로그래밍 언어와 유사한 블록 지향 제어 구조를 가지고 있다. 조건은 괄호로 둘러싸고, 제어되는 블록은 중괄호로 둘러싼다.

 label while ( cond ) { … }
 label while ( cond ) { … } continue { … }
 label for ( init-expr ; cond-expr ; incr-expr ) { … }
 label foreach var ( list ) { … }
 label foreach var ( list ) { … } continue { … }
 if ( cond ) { … }
 if ( cond ) { … } else { … }
 if ( cond ) { … } elsif ( cond ) { … } else { … }

단일 문장만 제어될 때는 문장 수정자가 더 간결한 문법을 제공한다.

 statement if cond ;
 statement unless cond ;
 statement while cond ;
 statement until cond ;
 statement foreach list ;

단락 논리 연산자는 표현식 수준에서 제어 흐름에 영향을 미치는 데 일반적으로 사용된다.

 expr and expr
 expr && expr
 expr or expr
 expr || expr

("and" 및 "or" 연산자는 && 및 ||와 유사하지만 우선순위가 낮아 전체 문장을 제어하는 데 더 쉽게 사용할 수 있다.)

흐름 제어 키워드 next(C의 continue에 해당), last(C의 break에 해당), return, redo는 표현식이므로 단락 연산자와 함께 사용할 수 있다.

펄에는 각각 두 가지 형태를 가진 두 가지 암시적 반복 구조도 있다.

 results = grep { … } list
 results = grep expr, list
 results = map { … } list
 results = map expr, list

grep은 제어되는 블록이나 표현식이 참으로 평가되는 리스트의 모든 요소를 반환한다. map은 리스트의 각 요소에 대해 제어되는 블록이나 표현식을 평가하고 결과 값의 리스트를 반환한다. 이러한 구조는 간단한 함수형 프로그래밍 스타일을 가능하게 한다.

5.10.0 릴리스 이전에는 펄 5에 Switch 문이 없었다. 5.10.0부터 given/when이라는 다중 분기 문이 다음 형식으로 제공된다.

 use v5.10; # 새로운 5.10 함수를 가져오려면 반드시 있어야 함
 given ( expr ) { when ( cond ) { … } default { … } }

문법적으로 이 구조는 다른 언어에서 발견되는 Switch 문과 유사하게 작동하지만 몇 가지 중요한 차이점이 있다. 가장 큰 차이점은 switch/case 구조와 달리 given/when 문은 명시적으로 정의된 break 명령을 기다리지 않고 첫 번째 성공적인 분기 후에 실행을 중단한다는 것이다. 반대로 switch 동작을 에뮬레이션하려면 명시적인 continue가 필요하다.

Perl 5.10을 사용하지 않는 사람들을 위해 Perl 문서는 다른 제어 구조를 사용하여 동일한 효과를 얻을 수 있는 6가지 방법을 설명한다. 또한 라쿠 언어의 기능을 모델로 한 Switch 모듈도 있다. 이는 소스 필터를 사용하여 구현되므로 비공식적으로 사용이 권장되지 않는다.

펄에는 goto label 문이 포함되어 있지만 거의 사용되지 않는다. 다른 언어에서 goto가 필요한 상황은 펄의 다양한 흐름 제어 옵션 때문에 자주 발생하지 않는다.

또한 꼬리 재귀를 수행하는 goto ⊂ 문도 있다. 이것은 현재 서브루틴을 종료하고 지정된 sub를 즉시 호출한다. 이는 호출자가 Perl 자체보다 더 효율적인 스택 관리를 수행할 수 있는 상황(일반적으로 현재 스택에 변경이 필요 없는 경우)과 깊은 재귀에서 사용되며, 꼬리 호출은 반환 시 스코프/스택 관리 오버헤드를 피하므로 성능에 상당한 긍정적인 영향을 미칠 수 있다.

서브루틴
서브루틴은 sub 키워드로 정의되며 단순히 이름을 지정하여 호출한다. 해당 서브루틴이 아직 선언되지 않은 경우, 호출하려면 함수 이름 뒤에 괄호를 붙이거나 함수 이름 앞에 앰퍼샌드(&)를 붙여야 한다. 그러나 괄호 없이 &를 사용하면 현재 서브루틴의 인수가 호출되는 서브루틴으로 암시적으로 전달되며, 괄호와 함께 &를 사용하면 프로토타입이 무시된다.

# 서브루틴 호출

# 서브루틴이 코드의 나중에 정의되는 경우 여기서는 괄호가 필요하다.
foo();
&foo # (이것도 작동하지만, 서브루틴에 전달되는 인수에 관한 다른 결과가 있다.)

# 서브루틴 정의
sub foo { … }

foo; # 여기서는 괄호가 필요하지 않다.

서브루틴 이름 뒤에 인수 목록을 제공할 수 있다. 인수는 스칼라, 목록 또는 해시가 될 수 있다.

foo $x, @y, %z;
서브루틴의 매개변수는 개수나 유형을 선언할 필요가 없으며, 실제로 호출마다 달라질 수 있다. 매개변수 유효성 검사는 서브루틴 내부에서 명시적으로 수행해야 한다.

배열은 요소로 확장되고, 해시는 키/값 쌍 목록으로 확장되며, 이 모든 것은 스칼라의 단일 평면 목록으로 서브루틴에 전달된다.

전달된 인수는 모두 특수 배열 @_에서 서브루틴에서 사용할 수 있다. @_의 요소는 실제 인수에 대한 참조이다. @_의 요소를 변경하면 해당 인수가 변경된다.

@_의 요소는 일반적인 방식으로 첨자를 사용하여 접근할 수 있다.

$_[0], $_[1]

그러나 결과 코드는 읽기 어려울 수 있으며, 매개변수는 참조에 의한 호출 의미론을 가지므로 바람직하지 않을 수 있다.

일반적인 관용구 중 하나는 @_를 명명된 변수 목록에 할당하는 것이다.

my ($x, $y, $z) = @_;

이것은 니모닉 매개변수 이름을 제공하고 값에 의한 호출 의미론을 구현한다. my 키워드는 다음 변수들이 포함하는 블록에 어휘적으로 스코프된다는 것을 나타낸다.

다른 관용구는 @_에서 매개변수를 shift하는 것이다. 이는 서브루틴이 단 하나의 인수를 취하거나 객체 지향 모듈에서 $self 인수를 처리하는 데 특히 일반적이다.

my $x = shift;

서브루틴은 명명된 인수를 시뮬레이션하기 위해 @_를 해시에 할당할 수 있다. 이는 세 개 이상의 매개변수를 가질 가능성이 있는 서브루틴에 대해 Perl Best Practices에서 권장된다.

sub function1 {
 my %args = @_;
 print "'x' argument was '$args{x}'\n";
}
function1( x => 23 );

서브루틴은 값을 반환할 수 있다.

return 42, $x, @y, %z;

서브루틴이 return 문을 통해 종료되지 않으면, 서브루틴 본문 내에서 마지막으로 평가된 표현식을 반환한다. 반환 값의 배열과 해시는 인수와 마찬가지로 스칼라 목록으로 확장된다.

반환된 표현식은 서브루틴의 호출 컨텍스트에서 평가된다. 이는 부주의한 사람들을 놀라게 할 수 있다.

sub list { (4, 5, 6) }
sub array { @x = (4, 5, 6); @x }

$x = list; # 6 반환 - 목록의 마지막 요소
$x = array; # 3 반환 - 목록의 요소 수
@x = list; # (4, 5, 6) 반환
@x = array; # (4, 5, 6) 반환

서브루틴은 wantarray 함수를 사용하여 호출 컨텍스트를 파악할 수 있다.

sub either {
 return wantarray ? (1, 2) : 'Oranges';
}

$x = either; # "Oranges" 반환
@x = either; # (1, 2) 반환
익명 함수

정규 표현식
펄 언어에는 정규 표현식(RE, 또는 regexes)을 작성하기 위한 특수화된 구문이 포함되어 있으며, 인터프리터에는 문자열을 정규 표현식과 일치시키는 엔진이 포함되어 있다. 정규 표현식 엔진은 퇴각검색 알고리즘을 사용하여, 단순한 패턴 매칭에서 문자열 캡처 및 대체까지 기능을 확장한다. 정규 표현식 엔진은 헨리 스펜서가 작성한 정규 표현식에서 파생되었다.

펄 정규 표현식 구문은 원래 유닉스 버전 8 정규 표현식에서 가져왔다. 그러나 펄의 첫 번째 릴리스 이전에 분기되었으며 그 이후로 훨씬 더 많은 기능을 포함하도록 성장했다. PHP, 루비, 자바, 마이크로소프트의 닷넷 프레임워크, 아파치 HTTP 서버와 같은 다른 많은 언어와 응용 프로그램이 이제 펄 호환 정규 표현식을 POSIX 정규 표현식보다 채택하고 있다.

정규 표현식 문법은 역사적으로 매우 간결하다. 초기 정규 표현식 방언은 글로브보다 표현력이 약간 더 많았고, 문법은 표현식이 일치하는 텍스트와 유사하도록 설계되었다. 이는 지원되는 몇 가지 주장을 표현하기 위해 단일 구두점 문자나 한 쌍의 구분 문자를 넘지 않도록 하는 것을 의미했다. 시간이 지남에 따라 정규 표현식의 표현력은 엄청나게 성장했지만, 문법 설계는 결코 수정되지 않았고 계속해서 구두점에 의존하고 있다. 결과적으로 정규 표현식은 난해하고 매우 밀집될 수 있다.

사용

m// (매치) 연산자는 정규 표현식 매치를 도입한다. (여기 모든 예시에서처럼 슬래시로 구분되는 경우, 간결성을 위해 선행하는 m은 생략될 수 있다. 다음 모든 예시에서처럼 m이 있는 경우, 슬래시 대신 다른 구분 기호를 사용할 수 있다.) 가장 간단한 경우, 다음과 같은 표현식은

$x =~ /abc/;

문자열 $x가 정규 표현식 abc와 일치하는 경우에만 참으로 평가된다.

반면에 s/// (대체) 연산자는 검색 및 대체 작업을 지정한다.

$x =~ s/abc/aBc/; # b를 대문자로

정규 표현식의 또 다른 용도는 split 함수의 구분 기호를 지정하는 것이다.

@words = split /,/, $line;

split 함수는 정규 표현식과 일치하는 것에 의해 분리된 문자열 부분들의 목록을 생성한다. 이 예제에서는 한 줄이 쉼표로 구분된 부분들의 목록으로 나뉘고, 이 목록은 @words 배열에 할당된다.

 문법 

수정자

펄 정규 표현식은 수정자를 사용할 수 있다. 이들은 표현식의 의미를 수정하는 단일 문자 접미사이다.

$x =~ /abc/i; # 대소문자 구분 없는 패턴 매치
$x =~ s/abc/aBc/g; # 전역 검색 및 대체

정규 표현식의 간결한 문법은 이해하기 어렵고 암호처럼 만들 수 있기 때문에, 프로그래머가 더 읽기 쉬운 정규 표현식을 작성하는 데 도움을 주기 위해 펄에 /x 수정자가 추가되었다. 이 수정자는 프로그래머가 정규 표현식 내부에 공백과 주석을 추가할 수 있게 한다.

$x =~ /
 a # 'a' 매치
 . # 임의의 문자
 c # 그리고 'c' 문자
 /x;

캡처

정규 표현식의 일부는 괄호로 묶을 수 있으며, 일치하는 문자열의 해당 부분은 캡처된다. 캡처된 문자열은 순차적인 내장 변수 $1, $2, $3, …에 할당되며, 캡처된 문자열 목록은 일치의 값으로 반환된다.

$x =~ /a(.)c/; # 'a'와 'c' 사이의 문자 캡처

캡처된 문자열 $1, $2, $3, …는 나중에 코드에서 사용할 수 있다.

펄 정규 표현식은 /e 수정자를 사용하여 캡처된 일치 항목에 내장 또는 사용자 정의 함수를 적용할 수도 있다.

$x = "Oranges";
$x =~ s/(ge)/uc($1)/e; # OranGEs
$x .= $1; # 이전 문장에서 일치한 내용으로 $x를 추가: OranGEsge

객체

펄에서 객체 지향 프로그래밍 코드를 작성하는 방법은 여러 가지가 있다. 가장 기본적인 방법은 "축복받은(blessed)" 참조를 사용하는 것이다. 이것은 모든 유형의 참조를 주어진 패키지에 속하는 것으로 식별하여 작동하며, 패키지는 축복받은 참조에 대한 메서드를 제공한다. 예를 들어, 2차원 점은 다음과 같이 정의할 수 있다.

sub Point::new {
 # 여기서 Point->new(4, 5)는 $class가 'Point'가 되도록 한다.
 # 이는 서브클래싱을 지원하는 변수이다 (perloop manpage 참조).
 my ($class, $x, $y) = @_;
 bless [$x, $y], $class; # 암시적 반환
}

sub Point::distance {
 my ($self, $from) = @_;
 my ($dx, $dy) = ($$self[0] - $$from[0], $$self[1] - $$from[1]);
 sqrt($dx * $dx + $dy * $dy);
}

이 클래스는 new()를 호출하여 인스턴스를 생성하고, 해당 인스턴스에 distance를 호출하여 사용할 수 있다.

my $p1 = Point->new(3, 4);
my $p2 = Point->new(0, 0);
print $p1->distance($p2); # 5를 출력

많은 최신 펄 애플리케이션은 Moose 객체 시스템을 사용한다. Moose는 메타 객체 프로토콜인 Class::MOP 위에 구축되어 모든 Moose 사용 클래스에 대한 완전한 자기 성찰 기능을 제공한다. 따라서 간단한 API를 사용하여 클래스의 속성, 부모, 자식, 메서드 등을 질문할 수 있다.

Moose 클래스:
 클래스는 0개 이상의 속성을 가진다.
 클래스는 0개 이상의 메서드를 가진다.
 클래스는 0개 이상의 슈퍼클래스(일명 부모 클래스)를 가진다. 클래스는 슈퍼클래스에서 상속한다.
 클래스는 0개 이상의 역할을 수행하며, 서브클래싱 없이 미리 정의된 기능을 클래스에 추가할 수 있는 기능을 추가한다.
 클래스는 생성자와 소멸자를 가진다.
 클래스는 메타클래스를 가진다.
 클래스는 0개 이상의 메서드 수정자를 가진다. 이러한 수정자는 자체 메서드, 조상으로부터 상속된 메서드, 또는 역할에 의해 제공되는 메서드에 적용될 수 있다.

Moose 역할:
 역할은 클래스가 수행하는 것으로, 다른 객체 지향 프로그래밍 언어의 믹스인 또는 인터페이스와 다소 유사하다. 믹스인 및 인터페이스와 달리 역할은 개별 객체 인스턴스에 적용될 수 있다.
 역할은 0개 이상의 속성을 가진다.
 역할은 0개 이상의 메서드를 가진다.
 역할은 0개 이상의 메서드 수정자를 가진다.
 역할은 0개 이상의 필수 메서드를 가진다.

예시

Moose의 MooseX::Declare 확장자를 사용하여 작성된 클래스의 예시:

use MooseX::Declare;

class Point3D extends Point {
 has 'z' => (isa => 'Num', is => 'rw');

 after clear {
 $self->z(0);
 }
 method set_to (Num $x, Num $y, Num $z) {
 $self->x($x);
 $self->y($y);
 $self->z($z);
 }
}

이것은 Point3D라는 클래스로, Moose 예제에서 설명된 Point라는 다른 클래스를 확장한다. 이 클래스는 기본 클래스에 새로운 속성 z를 추가하고, set_to 메서드를 재정의하며, clear 메서드를 확장한다.

 각주 

 외부 링크 
 Perl 튜토리얼
 약 2시간 30분 만에 펄 배우기
 PerlMonks 펄 지식과 코딩 팁 공유에 전념하는 커뮤니티.

분류:펄