펄 (프로그래밍 언어)

From PGWiki
(Redirected from )

흐름 제어

last, next, redo, continue, break, goto 등이 있다.

while (EXPR) {
    ### redo는 항상 여기로 온다.
    do_something;
} continue {
    ### next는 항상 여기로 온다.
    do_something_else;
    # 이후에 다시 EXPR을 평가하기 위해 위로 돌아간다.
}

breakgiven() 내에서 탈출할 때 사용된다.

파일

간단하게 파일 읽기

파일 들이마시기
print slurp("/proc/meminfo", "/proc/cpuinfo");

1;

sub slurp
{
    local @ARGV = @_;
    local @_ = <>;
    @_;
}


EOF의 제어

while 루프에서 텍스트 파일 읽기
open my $fh, '<', 'data.txt' or die;
while (my $line = <$fh>)
{
    ...;
}
더 이상 읽을 내용이 없으면 undef를 반환하므로, 따로 EOF 검사가 필요하지 않다.
리스트 컨텍스트에 파일 읽어오기
open my $fh, '<', 'data.txt' or die;
my @lines = <$fh>;
마찬가지로, 입력이 고갈되면 알아서 읽기 연산이 종료된다.
바이너리 파일 읽기
open my $fh, '<:raw', $file or die;
my $buf;
while (read($fh, $buf, 1000))
{
    ...;
}
read() 함수는 EOF에서 0을 반환하므로, 따로 검사할 필요가 없다.


select를 사용한 기본 파일 핸들 변경

select (F_STATUS);  # 기본 핸들러를 F_STATUS로 변경
$~ = "FORMAT_STAT";
write;

select (STDOUT);    # 기본 핸들러를 표준 출력으로 복원


버퍼


모듈(패키지)

use, require의 비교

설치된 모듈 확인하기

  1. 라이브러리를 이용하는 방법
    • perldoc perlmodlib
    • perldoc perllocal
  2. 간단한 코드를 이용하는 방법
    perl -MFile::Find=find -MFile::Spec::Functions -Tlw
         -e 'find { wanted => sub { print canonpath $_ if /\.pm\z/ },
             no_chdir => 1 }, @INC'
    
  3. ActivePerl에서는?
    • ppm query

설치된 모듈 지우기

아래의 코드를 활용하면 된다.

# uninstall_perl_module.pl from PerlTricks.com

use 5.14.2;
use ExtUtils::Installed;
use ExtUtils::Packlist;

# Exit unless a module name was passed
die ("Error: no Module::Name passed as an argument. E.G.\n\t perl $0 Module::Name\n") unless $#ARGV == 0;

my $module = shift @ARGV;

my $installed_modules = ExtUtils::Installed->new;

# iterate through and try to delete every file associated with the module
foreach my $file ($installed_modules->files($module)) {
    print "removing $file\n";
    unlink $file or warn "could not remove $file: $!\n";
}

# delete the module packfile
my $packfile = $installed_modules->packlist($module)->packlist_file;
print "removing $packfile\n";
unlink $packfile or warn "could not remove $packfile: $!\n";

# delete the module directories if they are empty
foreach my $dir (sort($installed_modules->directory_tree($module))) {
    print("removing $dir\n");
    rmdir $dir or warn "could not remove $dir: $!\n";
}

Exporter

한 패키지가 자신의 심볼(변수나 함수 등)을 다른 패키지에 내보낼 수 있도록 만드는 방법에는 Exporter 패키지를 사용하거나 이름공간을 수정하는 방법 등이 있다.

아래의 두 명령은 내부적으로 동일한 결과를 낳는다.

# use 명령을 사용한 패키지 가져오기
use Module LIST;

# 위 명령과 등가
BEGIN { require 'Module.pm'; 'Module'->import( LIST ); }

즉, 패키지 내에서 정의된 import라는 함수를 수행한다는 말이다.

따라서 아래 코드는 패키지의 모든 서브루틴을 내보내는 효과를 가져온다.

sub import {
    no strict 'refs';
    my $caller = caller;

    while (my ($name, $symbol) = each %{__PACKAGE__ . '::'})
    {
        next if $name eq 'BEGIN';       # don't export BEGIN blocks
        next if $name eq 'import';      # don't export this sub
        next unless *{$name}{CODE};     # export subs only

        my $imported = $caller . '::' . $name;
        *{ $imported } = \*{ $symbol };
    }
}

Exporter는 이러한 과정을 일반화하여 좀 더 쉽게 사용할 수 있도록 만든 패키지다.

아래 코드는 이 패키지를 활용하여 모든 스칼라들을 내보내는 코드다.

use Exporter;
our @ISA = qw/Exporter/;

our @EXPORT = do {
    no strict 'refs';
    map { '$' . $_ } keys %{'패키지명::'};
};

OS에 따라 다른 패키지 불러오기

package MyModule;

# Module::Load가 아닌 다른 패키지를 불러오거나 상속하는 등의 방법을 사용할 수도 있다.
use Module::Load;

BEGIN
{
    my $impl = "MyModule::$^O";
    load $impl;
    our @ISA = $impl;
}

sub new
{
    my ($pkg, $handle) = @_;
    my $self = bless {}, $pkg;
    return $self;
}

sub DESTROY
{
    my $self = shift;
}

...

1;

__END__

모듈의 위치를 찾지 못하는 경우

  1. use lib
    use lib '/home/foobar/code'; # 라이브러리의 위치 명시
    use My::Module;              # 사용할 모듈 지정
    
  2. perl 호출 시 설정
    # perl -I<모듈의 위치하는 디렉터리 경로명> <펄 스크립트 파일>
    

레퍼런스

특수 참조 기호

  • {+...} - ...이 상수 혹은 내부 키워드일 때, 이를 연관 배열 참조를 위한 키로 해석되도록 한다.
  • +{ - 강제로 연관 배열 참조로 해석되도록 한다.
  • {; - 강제로 코드 블록 참조로 해석되도록 한다.

ref 키워드

use 5.010;
say 'SCALAR:  ', ref \undef;
say 'ARRAY:   ', ref [1..5];
say 'HASH:    ', ref { key => 'value' };
say 'CODE:    ', ref sub {};
say 'REF:     ', ref \\undef;
say 'GLOB:    ', ref \*_;
say 'LVALUE:  ', ref \substr "abc", 1, 2;
say 'LVALUE:  ', ref \vec 42, 1, 2;
say 'FORMAT:  ', ref *STDOUT{FORMAT}; # needs declaration below
say 'IO:      ', ref *STDIN{IO};   # actually prints IO::Handle
say 'VSTRING: ', ref \v5.10.0;
say 'Regexp:  ', ref qr/./;

스마트 매치

펄의 연산자 중에는 편의를 위한 스마트 매치라는 연산자가 있다.

"~~"가 바로 그 녀석인데, 간단한 예는 아래와 같다.

my @a = qw/abc def efg ghy klm ghn/;
my @b = qw/def ghy jgk lom com klm/;

my $flag;

foreach my $item (@a)
{
    $flag = @b~~$item ? 0 : 1;
    last if !$flag;
}

이 외에도 해시와 배열, 코드 간의 서로 다른 자료형에 대해 일정한 규칙을 갖는 비교를 수행할 수 있다.

주로 두 배열의 교집합이나 부분집합을 만들어내기에 적합하다.

상세한 내용은 perlop를 참조.


배열 비교

스마트 매치나 List::MoreUtils나 Array::Compare, Data::Compare 등과 같이 비교 기능이 있는 패키지를 쓴다던가, 하다 못해 brute force로 직접 구현해도 되지만, 아래와 같이 해시와 "grep", "map"를 이용해서도 간단히 비교할 수 있다.

해시와 grep, map을 사용한 배열의 비교
sub cmp_array
{
    my @x = @{$_[0]};
    my @y = @{$_[1]};

    my %basket = ();
    my @isect  = ();

    map { $basket{$_} = 1 } @x;
    @isect = grep { $x{$_} } @y;

    return (@x == @isec);
}

my @array1 = ("a", "b", "c");
my @array2 = ("b", "c", "d");

return cmp_array(\@array1, \@array2);

요약하자면, map으로 원본 배열의 원소를 키로 하는 해시를 생성해서 비교 대상의 배열의 원소로 grep을 한다는 것!


코드 난독화

  • Filter::Crypto
    코드 자체를 OpenSSL로 암호화하는 모듈이다.


약한 참조

  • Scalar::Util
    약한 참조 구현 시에 사용 가능한 모듈.


스택 참조

caller 함수를 호출하면 현재 스택에 있는 내용들을 확인할 수 있다.

use Data::Dumper;

sub test
{
    my @test1 = caller(0);
    my @test2 = caller(1);

    print Data::Dumper->Dump([\@test1, \@test2]);

    test1();
}

sub test1
{
    my @test1 = caller(0);
    my @test2 = caller(1);

    print Data::Dumper->Dump([\@test1, \@test2]);

    test2();
}

sub test2
{
    my @test1 = caller(0);
    my @test2 = caller(1);

    print Data::Dumper->Dump([\@test1, \@test2]);
}

test();

---- 결과 ----

$VAR1 = [
          'main',
          'caller.pl',
          57,
          'main::test',
          1,
          undef,
          undef,
          undef,
          1794,
          'UUUUUUUUUUUU',
          undef
        ];
$VAR2 = [];

$VAR1 = [
          'main',
          'caller.pl',
          36,
          'main::test1',
          1,
          undef,
          undef,
          undef,
          1794,
          'UUUUUUUUUUUU',
          undef
        ];
$VAR2 = [
          'main',
          'caller.pl',
          57,
          'main::test',
          1,
          undef,
          undef,
          undef,
          1794,
          'UUUUUUUUUUUU',
          undef
        ];

$VAR1 = [
          'main',
          'caller.pl',
          46,
          'main::test2',
          1,
          undef,
          undef,
          undef,
          1794,
          'UUUUUUUUUUUU',
          undef
        ];
$VAR2 = [
          'main',
          'caller.pl',
          36,
          'main::test1',
          1,
          undef,
          undef,
          undef,
          1794,
          'UUUUUUUUUUUU',
          undef
        ];


Data::Dumper에서 키 정렬하여 출력

# 정렬된 자료 구조로 출력
$Data::Dumper::Sortkeys = 1;

# 이 코드는 위 코드와 등가...
$Data::Dumper::Sortkeys = \sub {
    my ($hash) = @_;
    return [sort keys %$hash];
};

진행 표시줄

Windows와 연동하기

이름 공간 별칭

use Package::Alias Fbbq => 'Foo::Barista::Bazoo::Qux';

use aliased 'Foo::Barista::Bazoo::Qux' => 'Fbbq';

use namespace::alias 'Foo::Barista::Bazoo::Qux' => 'Fbbq';

sub Fbbq { Foo::Barista::Bazoo::Qux:: }

local *fbbq_scalar = \$Foo::Barista::Bazoo::Qux::the_scala

같이 보기


바깥 고리


크리스마스 달력


Tutorials

Tips


Moose

XS


Q&A


References


문서 자료

임시 저장

#!/usr/bin/perl
# begincheck
print         "10. Ordinary code runs at runtime.\n";
END { print   "16.   So this is the end of the tale.\n" }
INIT { print  " 7. INIT blocks run FIFO just before runtime.\n" }
UNITCHECK {
    print       " 4.   And therefore before any CHECK blocks.\n"
}
CHECK { print " 6.   So this is the sixth line.\n" }
    print         "11.   It runs in order, of course.\n";
BEGIN { print " 1. BEGIN blocks run FIFO during compilation.\n" }
END { print   "15.   Read perlmod for the rest of the story.\n" }
CHECK { print " 5. CHECK blocks run LIFO after all compilation.\n" }
INIT { print  " 8.   Run this again, using Perl's -c switch.\n" }
    print         "12.   This is anti-obfuscated code.\n";
END { print   "14. END blocks run LIFO at quitting time.\n" }
BEGIN { print " 2.   So this line comes out second.\n" }
UNITCHECK {
    print " 3. UNITCHECK blocks run LIFO after each file is compiled.\n"
}
INIT { print  " 9.   You'll see the difference right away.\n" }
print         "13.   It merely _looks_ like it should be confusing.\n";
__END__