2013年3月19日火曜日

連続する文字に一致させる

AAやBB等、同じ文字が連続する場合に一致させるには、正規表現の後方参照を使います。
後方参照とは、括弧の中にマッチしたテキストを表します。
例えば、\1は最初に一致した括弧の文字を表し、\2は2番めに一致した括弧の文字を表します。


my @strings = (

"すす、好きです",
"あ、愛してる",
"ABBA",
"Change");

foreach my $aStr(@strings) {
if ($aStr=~/(\S)\1/) {
print "$aStr\n";
}
}



上記では、スペース以外の文字を表す\Sを括弧でくくり、後方参照\1で一致した括弧の文字と一致するかをチェックすることで文字の連続をチェックしています。

結果は以下のようになります。

すす、好きです
ABBA



2013年3月18日月曜日

エラー発生時に自動的に死亡するようにする

Perlのバージョン5.10以降からは「use autodie」を付けることでファイルIO等、エラーが発生した時に自動的に死亡するようにすることができるようになりました。

従来


open $IN, "<:utf8", $ARGV[0] or die "Can't open file $!"; # ファイルがオープン出来なければ死亡することを明示


上記ではオープンのたびに正常にオープンできたかチェックし、エラーなら死亡するようにしています。


新しいバージョン


use autodie;
open $IN, "<:utf8", $ARGV[0]; # もうエラーについて書かなくても良い!



当然、死亡理由等、必要な情報を吐き出して死亡してくれます。


出力フォーマットを指定する

Perlで文字列を出力するには、printを使いますが、フォーマットを指定して出力するには、printfを使います。

%dは10進整数を表示し、%sは文字を表示します。


my $num=55.55;
printf "数値:%d 文字:%s\n", $num, $num;



%dは小数以下は切り捨てられます。結果は以下のようになります。


数値:55 文字:55.55





%gは、perlが小数、整数、浮動少数等、状況に応じて適宜判断してくれます。


printf "適宜当てはめた値:%g %g %g\n", 0.5, 0.0000000234567, 10;



結果は以下のようになります。


適宜当てはめた値:0.5 2.34567e-08 10





文字(%s)や整数(%d)等、%とdやs等の間に数字を指定することで桁数を指定できます。


my $num=55.55;
printf "数値:%6d 文字:%10s 適宜当てはめた値:%6g\n", $num, $num, $num;



結果は以下のようになります。


数値:    55 文字:     55.55 適宜当てはめた値: 55.55


上記は桁数を指定したので、黄色い部分にスペースが挿入されています。


更に、空白ではなく0で埋めたり、左寄せしたり、四捨五入もできます。パーセント自体を出力する時は、%%と2個連続させます。



my $num=55.55;
printf "0で埋める:%06d 左寄せ:%-6d 四捨五入または浮動少数:%4.0f %%\n", $num, $num, $num;




結果は以下のようになります。


0で埋める:000055 左寄せ:55     四捨五入または浮動少数:56 %









静的変数

Perlで静的変数(C言語でいうところのstatic変数)を使用するには、stateを使用します。
Perlのバージョン5.10以降の機能なのでuse 5.010を付けることに注意が必要です。
また、バージョン5.14時点ではstateを配列(リスト)や辞書(ハッシュ)につけることはできないようです。



#!/opt/local/bin/perl
use warnings;
use 5.010;

print "号令!\n";
while (my $line = <STDIN>) {
if (!defined($line)) { last; };
&marine;
}

sub marine {
state $sailor_number = 0;
$sailor_number++;
print "I'm sailor $sailor_number!\n";
}



結果は以下のようになります。


号令!

I'm sailor 1!

I'm sailor 2!

I'm sailor 3!

I'm sailor 4!

I'm sailor 5!

I'm sailor 6!

I'm sailor 7!





foreachで使用した変数は元に戻る

Perlでは、foreachループで使用した変数は自動的に元の値に戻ります。

例えば、


@strings = qw(neko inu tori);
$str = "empty";
foreach $str(@strings) {
print "str=$str\n";
}
print "str=$str\n"; # ループ内の変更は保持されない



結果は以下のようになります。


str=neko
str=inu
str=tori
str=empty


$strの値はループ内で変更されますが、ループを抜けると元の値に戻っています。

当たり前ですが、for文ではこのようなことはありません。
念のためfor文のループ処理も書いておきます。


@strings = qw(neko inu tori);

$count = 0;
$str = "empty";
for($count=0 ; $count < scalar(@strings) ; $count++) {
$str = $strings[$count];
print "str=$str\n";
}
print "count=$count\n"; # ループ内の変更が反映される
print "str=$str\n"; # ループ内の変更が反映される




結果は以下のようになります。


str=neko
str=inu
str=tori
count=3
str=tori



for文のループ内で変更された$strと$countの値は、ループを抜けても最後に格納された値が反映されています。(自動的に元には戻りません。)


2013年3月8日金曜日

連想配列からキーと値の組みを取り出す

連想配列(ハッシュ)からキーと値を一度に取り出すには、eachを使います。



%hash = (
'D' => 4,
'E' => 9,
'C' => 1,
'A' => 5,
'B' => 8,
);

while (@key_value = each %hash) { # 辞書からキーと値の組みを取り出す
print "$key_value[0]:$key_value[1]\n";
}



eachでは「キー」と「値」の配列が取得できます。
結果は以下のようになります。


A:5
C:1
D:4
B:8
E:9



2013年3月7日木曜日

連想配列(ハッシュ)をキー順にソート

ハッシュをキー順にソートするには、以下のようにします。


%hash = (
'D' => 4,
'E' => 9,
'C' => 1,
'A' => 5,
'B' => 8,
);


@sorted_key = sort { $a cmp $b } keys %hash; # ハッシュをキーの昇順にソート

foreach $aKey(@sorted_key) {
print "[$aKey]:$hash{$aKey}\n";
}


結果は以下のようになります。


[A]:5
[B]:8
[C]:1
[D]:4
[E]:9



注意が必要なのは、連想配列自体に順序があるわけではないので、連想配列の中身の順序が変わるわけではないということです。
ソートするのはあくまでもキーについてだけです。

配列に配列を挿入する

配列に配列を挿入するには、spliceを使います。



my @foo = qw(ari hituji inko cat dog);
my @baa = qw(sky tree note memo);

splice(@foo, 1, 0, @baa); # 配列fooの1番目に配列baaを挿入

my $i = 0;
foreach my $item(@foo) {
    print "[$i]:$item\n";
    $i++;
}



結果は以下のようになります。


[0]:ari
[1]:sky
[2]:tree
[3]:note
[4]:memo
[5]:hituji
[6]:inko
[7]:cat
[8]:dog




配列の要素を削除

配列の要素を削除するには、spliceを使います。



my @foo = qw(ari hituji inko cat dog);

splice(@foo, 2, 2); # 2番目の要素から2つ削除

my $i = 0;
foreach my $item(@foo) {
    print "[$i]:$item\n";
    $i++;
}




結果は以下のようになります。


[0]:ari
[1]:hituji
[2]:dog




配列の全ての要素を加工する

配列の全ての要素を加工するには、mapを使います。


use utf8;
binmode STDIN, "utf8";
binmode STDOUT, "utf8";

my @foo = qw(ari hituji inko cat dog);

@hoge = map { my $hogehoge = $_."さん" } @foo;

my $i = 0;
foreach my $item(@hoge) {
    print "[$i]:$item\n";
    $i++;
}


結果は以下のようになります。


[0]:ariさん
[1]:hitujiさん
[2]:inkoさん
[3]:catさん
[4]:dogさん



複数配列から重複しない行のみ抽出する

複数配列から重複しない行のみ抽出するには、以下のようにgrepを使います。



#!/opt/local/bin/perl
use utf8;


my @foo = qw(ari hituji inko cat dog);
my @baa = qw(cat ebi kuma hituji tora cat);

my %count;
$count{$_}++ for (@foo, @baa); # @fooと@baaの要素の数を数える
@hoge = grep { $count{$_} < 2 } keys %count; # 要素数が2より小さいもののみ抽出

my $i = 0;
foreach my $item(@hoge) {
    print "[$i]:$item\n";
    $i++;
}





結果は以下のようになります。



[0]:ari
[1]:inko
[2]:dog
[3]:ebi
[4]:kuma
[5]:tora




複数配列から重複行を抽出する


複数配列から重複行を取り出すには、grepを使うと便利です。


#!/opt/local/bin/perl
use utf8;

my @foo = qw(ari hituji inko cat dog);
my @baa = qw(cat ebi kuma hituji tora);

my %seen;
my $i = 0;
my @hoge = grep { ++$seen{$_} == 2 } (@foo, @baa);
foreach my $item(@hoge) {
    print "[$i]:$item\n";
    $i++;
}


結果は以下のようになります。




[0]:cat
[1]:hituji




1行目

Perlに限った話ではありませんが、ソースファイルの1行目には以下の様な記述をよく見かけます。


#!/opt/local/bin/perl


これはperl言語特有のものではなく、Linux等のシェルのお約束です。
上記の様に「#!」から始まる記述があることで、例えば普段はperlのプログラムを起動する時に、

perl hoge.pl

のように起動しますが、上記の記述があれば

hoge.pl

で起動できるようになります。(もちろん実行権限が付与されている必要があります。)

上の例では、シェルが実行権限の付いたhoge.plを開きますが、最初の行に「#!」から始まる行があることで、以降の/opt/local/bin/perlを起動し、その引数として自ファイルを渡してくれるようになります。

ですので、必ず

perl hoge.pl

等のように起動する人にとっては「#!」の行はいらないことになります。
ただし、他の人が同ファイルを起動しようとして

「hoge.pl」で起動できない!なんじゃこりゃ!?

と困ることがあるかもしれないので、お約束として付けておくのが習慣になっているようです。

ちなみに、「#!」の行は必ず1行目にある必要があります。2行めとかにあってもシェルは認識してくれません。



2013年3月6日水曜日

配列を任意の文字列で区切って表示する

joinを使えば配列を任意の文字列で区切って表示することができます。



@foo = qw(A B C D E F);

print join("\t", @foo)."\n"; # 配列をタブ区切りで表示



結果は以下のようになります。

A B C D E F



配列をハッシュに変換する

配列を要素のキーを持つ連想配列(ハッシュ)にすることができます。



@foo = ('A', 'B', 'C');

# 配列を連想配列に変換
%hash = ();
$hash{$_} = 1 for @foo;

foreach $key(keys %hash) {
    print "$key:$hash{$key}\n";
}



結果は以下のようになります。


A:1
C:1
B:1



配列に要素が存在するかチェックする

配列に要素が存在するかチェックするには、foreachで1個づつチェックしても良いですが、以下のようにすればより短い行数ですみます。

grepを使用



@foo = ('A', 'B', 'C');

if (grep {$_ eq 'B'} @foo) {
    print "exists\n";
}




Listを使用


use List::Util;

@foo = ('A', 'B', 'C');

if (List::Util::first{$_ eq 'C'} @foo) {
    print "exists\n";
}




2013年3月5日火曜日

戻り値に配列を渡す

Perlでは、戻り値に配列を返す場合、そのまま返却することができます。


#!/opt/local/bin/perl;                                                                                                  

my @array = &getMyArray();
$i = 0;
foreach $ref(@array) {
    print "[$i]:$ref\n";
    $i++;
}

sub getMyArray {
    my @array = ('A', 'B', 'C');
    return @array;
}


結果は
[0]:A
[1]:B
[2]:C
と出力されます。

また、クラスを使う場合でもそのまま配列を返却することが可能です。

main.pl


#!/opt/local/bin/perl;                                                                                                  
package main;

use utf8;
use TestSub;
use Clone qw(clone);

my @foo = ('A','B','C');

my $s = TestSub->new(\@foo);
@foo = ();
my @array = $s->getYourArray();

$i = 0;
foreach $ref(@array) {
    print "[$i]:$ref\n";
    $i++;
}



TestSub.pm


package TestSub;                                                                                                        
use utf8;
use Clone qw(clone);

sub new {
    $self = shift; # クラス名を取得します
    $ref = shift;  # 配列への参照を受け取ります

    my $array = clone($ref); # 配列の参照をコピーします
    my @array2 = (4, 5, 6);  # もう1つ別のメンバ変数を作っています
    my $data = {             # メンバ変数を作っています
        ref => $array,
        ref2=> \@array2,
    };
    return bless $data, $self;
}

sub getYourArray {
    my $self = shift;
    my $result = $self->{ref2}; # メンバ変数から配列を取得
    return @$result; # 配列を返却
}

1;



結果は
[0]:4
[1]:5
[2]:6
と出力されます。



クラスのコンストラクタに配列を渡す

クラスのコンストラクタに配列を渡すには、以下のようにします。

main.pl


#!/opt/local/bin/perl;
package main;

use utf8;
use TestSub;
use Clone qw(clone);

@foo = ('A','B','C'); # 配列を作って

my $s = TestSub->new(\@foo); # コンストラクタに渡します
@foo = ();

$s->hello();


TestSubクラスを生成して配列(foo)を渡しています。
その後、TestSubクラスのhelloメソッドを呼び出しています。
helloメソッドは受け取った配列を表示するようにしています。

呼ばれる側のソースは以下のようになります。

TestSub.pm


package TestSub;                                                                                                        
use utf8;
use Clone qw(clone);

sub new {
    $self = shift; # クラス名を取得します
    $ref = shift;  # 配列への参照を受け取ります

    my $array = clone($ref); # 配列の中身をコピーします
    my @array2 = (4, 5, 6);  # もう1つ別のメンバ変数を作っています
    my $data = {             # メンバ変数を作っています
        ref => $array,        # 引数で渡された配列
        ref2=> \@array2,  # ついでにもう1つ配列
    };
    return bless $data, $self; # blessでメンバ変数とクラスを関連付けて返却
}

sub hello {
    my $self = shift; # クラス名を取得します
    my $test = $self->{ref}; # メンバ変数refを取得します
    foreach my $ref(@$test) {
        print "hello ref1:$ref\n";
    }  
    my $test2 = $self->{ref2};
    foreach my $ref(@$test2) {
        print "hello ref2:$ref\n";
    }  
}

1;


ミソとしては、配列の参照を受け取るだけなので、コンストラクタ内でコピーしてあげないといけない点です。上記ではcloneを呼び出して配列をコピーしています。(もちろんCloneを使わずに愚直に1要素づつコピーしてもOKです。)

実行結果は以下のようになります。


hello ref1:A
hello ref1:B
hello ref1:C
hello ref2:4
hello ref2:5
hello ref2:6






クラスを作る

Perlでクラスを実装するには、以下のようにpackageとuseを組み合わせます。

main.pl

#!/usr/opt/local/bin/perl;
package main;
use utf8;
use TestClass; # TestClassを使用する

$c = TestClass->new(); # TestClassを生成する
$c->hello();


呼び込むクラスのファイル名は・・・.pm(plではありません)とします。

TestClass.pm


package TestClass;                                                                                                      
use utf8;

sub new {
    my $self = shift; # Perlのお約束で引数の先頭は自動的にクラス名が入るので、shiftで取得します

    my $member = {  # メンバ変数を作ってます
        mem1=>1,
        mem2=>2,
        mem3=>3,
    };
    return bless $member, $self; # blessでメンバ変数とクラスを関連付けたものを返却します
}

sub hello {
    print "Hi! I'm TestClass.\n";
}

1; # パッケージの読み込み結果(1:成功)を返却する



呼び込まれる側のソースですが、package句でクラス名を宣言する他に、最後の行で「1;」を書いて置かなければならないのが忘れやすいので注意が必要です。




2013年3月4日月曜日

配列の参照を中身までコピーする

配列をコピーするには以下のように簡単に行えますが、
@foo = @bar;

配列の参照をコピーしたい場合は参照がコピーされるだけなので中身の値までコピーしてくれるわけではありません。


my @foo = ('A', 'B');
my @bar = \@foo; # 参照をコピーしても中身までコピーされるわけではない
@foo = (); # 参照元を初期化

foreach my $ref(@$bar) { # 参照元が初期化されているのでここでは何も出力されません
    print "$ref\n";
}


配列の参照を中身も含めてコピー(deep copy)したい場合は、foreach等を使って愚直に要素を1個ずつコピーするか、またはCloneモジュールを使います。


use Clone qw(clone);

my @foo = ('A', 'B');
my $bar = clone(\@foo); # 参照を中身までコピーする
@foo = (); # 参照元を初期化

foreach my $ref(@$bar) { # 参照元を初期化してもOK
    print "$ref\n";
}


結果は以下のようになります。


A
B



配列のコピー

@bar = @foo;


2013年2月19日火曜日

複数条件でソート

ソートは複数の条件を指定することができます。


%dic=();
$dic{'a'} = 0;
$dic{'b'} = 1;
$dic{'c'} = 2;
$dic{'d'} = 0;
$dic{'e'} = 2;
$dic{'f'} = 3;
$dic{'g'} = 0;

# 複数条件でソート
@sorted_keys = sort {$dic{$b} <=> $dic{$a} or $b cmp $a} keys %dic;                                        

foreach $key(@sorted_keys) {
    print "$key:$dic{$key}\n";
}


上の例では、ハッシュの値の降順にソートし、値が同じ場合はハッシュのキーの降順でソートする例です。

結果は以下のようになります。

f:3
e:2
c:2
b:1
g:0
d:0
a:0


orの左側に第一条件を指定し、もし第一条件が同じ値だった場合、次の条件をorの右側に指定します。条件が2つより多い場合は、更にorを続けて3番めの条件、4番めの条件、・・・というように続けることができます。

あまり条件が長くなると1行に書くのが分かりづらくなるようであれば、以下のようにソート条件を関数化することもできます。



%dic=();
$dic{'a'} = 0;
$dic{'b'} = 1;
$dic{'c'} = 2;
$dic{'d'} = 0;
$dic{'e'} = 2;
$dic{'f'} = 3;
$dic{'g'} = 0;

# ソート条件を関数呼び出しで
@sorted_keys = sort sort_routine keys %dic;

foreach $key(@sorted_keys) {
    print "$key:$dic{$key}\n";
}

sub sort_routine
{
    $dic{$b} <=> $dic{$a}
    or
    $b cmp $a
    or
    # もっといろんな条件
}







UTF8でファイル入出力

ファイルをオープンする時に文字コードを指定することができます。


open($ハンドル変数, "<:utf8", 入力ファイル名);
open($ハンドル変数, ">:utf8", 出力ファイル名);


上記でUTF8でファイルオープンします。

オープンのたびにいちいち指定するのが面倒な場合、以下のように指定することもできます。


use open IN => ":utf8";
use open OUT => ":utf8";


上記のIN / OUTはファイルハンドルではありません。
上記の指定で入力・出力フィルのオープン時にいちいち":utf8"等と文字コードを指定しなくてもよくなります。

また、上の2行もめんどうな時は、次のように1行ですますことも可能です。

use open IO => ":utf8"; # 入出力ファイルの文字コードをいっぺんに指定





ハッシュ(辞書)を値の順にソートする

ハッシュ(辞書)を値の昇順にソート


%hash = ();
$hash{'a'} = 1;
$hash{'b'} = 3;
$hash{'c'} = 5;
$hash{'d'} = 2;
$hash{'e'} = 4;

# ハッシュを値の昇順にソート
@sorted_key = sort {$hash{$a} <=> $hash{$b}} keys %hash;

foreach $key(@sorted_key) {
    print "$key:$hash{$key}\n";
}


実行結果は以下の様になります。


a:1
d:2
b:3
e:4
c:5



注意が必要なのは、ハッシュは配列のように数値のインデックスがあるわけではないのでsort関数で帰ってくるのはキーの配列だということです。

上記の$aと$bを入れ替えれば降順に出力されるようになります。


%hash = ();
$hash{'a'} = 1;
$hash{'b'} = 3;
$hash{'c'} = 5;
$hash{'d'} = 2;
$hash{'e'} = 4;

# ハッシュを値の降順にソート
@sorted_key = sort {$hash{$b} <=> $hash{$a}} keys %hash;

foreach $key(@sorted_key) {
    print "$key:$hash{$key}\n";
}



実行結果


c:5
e:4
b:3
d:2
a:1



日本語を処理する

日本語を処理するには、使用する文字コードを指定します。

use utf8; # 日本語(UTF8)を使用する

binmode STDIN, "utf8"; # 標準入力に日本語(UTF8)を使用する
binmode STDOUT, "utf8"; # 標準出力に日本語(UTF8)を使用する



サブルーチンの引数にファイルハンドルと配列を渡す

サブルーチンにファイルハンドルと配列を渡すには、以下のようにします。


open(my $OUT, ">", $ARGV[0]);

my @array = ('TEST1', 'TEST2', 'TEST3');

&out_array($OUT, @array);

close($OUT);


$ARGV[0]は上記プログラムを起動した時の最初の引数で、出力するファイル名を指定します。
$OUTはファイルハンドルを格納する変数です。
out_arrayはサブルーチン名で、引数に$OUTと配列@arrayを渡しています。

呼び出される側のサブルーチンは以下のようにして引数を取得できます。


sub out_array
{
    my $OUT = $_[0];
    my $item1 = $_[1];
    my $item2 = $_[2];
    my $item3 = $_[3];

    print $OUT "$item1\t$item2\t$item3\n"; # 指定された出力ファイルに"TEST1¥tTEST2¥tTEST3¥nと出力されます。
}





定数

Perlで定数を使用する時は、use constantを使用します。

use constant DATA => 0;
print DATA; # 0と出力されます


定数には文字列も指定可能です。

use constant DATA => 'TEST';
print DATA; # TESTと表示されます


defined

標準入力等から1行読み込む場合、<STDIN>とかを使いますが、
これ以上読み込む行が存在しない場合やファイルの最後の場合、undefが返ってきます。
入ってきた1行が存在しないか、最後かを判定する為にdefinedで判定します。

$line = <STDIN>;
if (!defined ($line)) {
   # 最後
}

undef

未初期化データの値はundefになります。
数値がundefの場合0として振る舞い、文字列がundefの場合は''(空文字)として振る舞います。

ディレクトリの存在チェックと作成

ディレクトリの存在チェック
if (!-d $dirpath)

ディレクトリの作成
mkdir $dirpath;

配列の要素数

配列の最後のインデックス
$#array;

配列の要素数
scalar(@array);

Perlの名前

Perlとは一応、Practical Extraction and Report Language(実用データ取得レポート作成言語)の略だそうです。

でも、Perl言語を作った作者Larry Wall氏によれば、最初にPerlという名前に決め、後で上の様なそれらしい名前を当てはめたんだそうです。

ですので、あくまでも1行目の名前は一説であり、Pathologically Eclectic Rubbish Lister(病的折衷主義のがらくた出力機)という説もあるんだそうです。ニヤリ。

はじめてのPerl

オライリー社から出版されている「はじめてのPerl」(通称リャマ本)に基づいてPerl言語を学習していくブログです。

よろしくお願いします。