Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

初心者から見たPerl言語I/Fは気持ち悪いが、たった一行の記述が素晴らしく強力な件について

プログラミングPerl〈VOLUME1〉

プログラミングPerl〈VOLUME1〉

追記

※2012/06/10 toku_bassさんからご指摘頂いた内容を載せておきます。

  • shebangの定義は #!/usr/bin/env perlの方が都合が良い。
  • 連想配列の定義でkeyを''や""で囲まないとエラーになる。
    • use strict無しでは''や""が無くてもそのまま実行されてしまいますが、use strictを使うと確かにエラーになる事を確認しました。よって必ずkeyは''や""で囲むべきです。
 #!/usr/bin/env perl
use strict;

my %hash = ( 'key0', 0, 'key1', 1, 'key2' ,2 );
foreach( sort keys %hash ) { 
    print "Key = $_ Value = $hash{$_} \n";
}
  • arrayの連番定義は1..9等を使うと良い。
  • 文字列の定義の場合はqw/ foo bar hoge /を使うと良い。''などで囲むのが面倒なので。
 #!/usr/bin/env perl

use strict;

my @array = ( 1 .. 9 );
foreach ( @array ) {
    print $_ . "\n";
}

@array = qw/ foo bar hoge /;
foreach ( @array ) {
    print $_ . "\n";
}
  • 連想配列のkeyに配列の値、連想配列の値を1と定義したい場合は @hash{@array} = (1) x @arrayと書く事ができる。
 #!/usr/bin/env perl
use strict;

my @array = ( 1 .. 9 );
my %hash = (); 
@hash{@array} = (1) x @array;
foreach( sort keys %hash ) { 
    print "Key = $_ Value = $hash{$_} \n";
}
Key = 1 Value = 1 
Key = 2 Value = 1 
Key = 3 Value = 1 
Key = 4 Value = 1 
Key = 5 Value = 1 
Key = 6 Value = 1 
Key = 7 Value = 1 
Key = 8 Value = 1 
Key = 9 Value = 1 

Perl プログラミング言語としてのInterFaceについて

Perlを勉強中の@yutakikucです。初めに断っておきますが決してPerlをdisってはいません。初心者の観点から感じた事を書いてみます。初心者がPerlを記述する上でイラつきを感じてしまうのが直感で文法の推測ができないという点です。これは他の言語に精通していればいるほど気味の悪いものになると思います。それほどにPerlの文法はとても特徴的です。例えば変数を宣言する時の配列(@)、ハッシュ(%)といった記号と実際にデータ要素を参照する時のスカラー($)を使用するという文法が気になります。(直感的には宣言時に使用した記号がそのまま使えると思ってしまう。) また特殊変数($_など)への値展開が自動でされる場面があり、急に特殊変数の記述が出てきても何を示しているのかが分からないこと、unless( ifの否定 )やnext( ループのcontinue )、last( ループのbreak ) が特徴的....など他にも多数あります。まとめると初心者からするとPerlのI/Fは分かりづらいといえます。しかしこれらの特徴的な文法を頭の中にしっかりと覚えてしまうとPerlは非常に強力なツールになるようです。この後で僕が勉強したPerlの文法について紹介して行きます。また初めにプログラミングPerlの冒頭に載っている著者/訳者の言葉をまとめておきます。

  • Perlはキュートである。キュートというには、いささか不格好なのだけれど。そう、ちょうどラクダみたいにね。
  • Perlはあなたの仕事をするための言語である。
  • コンピュータ言語は、どんなことが可能であるかという点では大差はないものの、どんなことを簡単に行えるかという点では大きな違いがある。
  • Perlを使う人々は、創造力が十分に発揮出来ると感じる。なぜなら、Perlには「表現の自由」があるからだ。

配列と連想配列の定義

配列と連想配列の定義が複雑なのがPerlの特徴です。単純な定義は配列で@連想配列で%を使って定義することが最もシンプルで分かりやすいですが、スカラー($)でリファレンスによる定義をする記述もあります。リファレンスとはC言語のポインタのようなものでデータの先頭番地のような値を参照することです。リファレンスから配列や連想配列形式の形に変換参照する事をデリファレンスと呼びます。これらを利用したシンプルな定義とリファレンスでの定義について下にまとめます。

配列のシンプルな定義

@(配列)または@(配列)と$(スカラー)を利用して配列を定義します。パターン1の方が一般的な記述だと思います。出力はData::Dumperを利用するかforeachで回すと良いと思います。下ではforeachを利用しています。やや話が逸れますが、foreachなどの繰り返しを定義すると配列の要素を特殊変数($_)に自動で定義してくれます。これにより余計な変数定義が不要になります。

#!/usr/local/bin/perl -w

use Data::Dumper;

# パターン1
my @array = ( 0, 1, 2 );

# パターン2
my @array = ();
$array[0] = 0;
$array[1] = 1;
$array[2] = 2;

foreach( @array ) {
    print $_ . "\n";  #要素を$_に自動的に展開してくれる
}
# print Dumper @array;
配列のリファレンス定義

配列のリファレンスを参照するには\@arrayを利用します。リファレンスを代入するにはスカラー($)を使います。リファレンスはただのアドレスなので、そのままforeachに渡すとエラーが出ます。foreach内で配列形式として参照するには配列の定義をそのまま入れるか、デリファレンスを使います。配列のデリファレンスは@{$array}のようになります。Data::Dumperの方はリファレンスをそのまま渡しても配列形式として出力します。

#!/usr/local/bin/perl -w

use Data::Dumper;

print "----array---- \n";

my @array = ( 0, 1, 2); 

# リファレンスを代入
my $array_ref = \@array;

=pod エラーになる定義
foreach( $array_ref ) {
    print $_ . "\n";
}
=cut

# デリファレンスを行い、参照する
foreach( @{$array_ref} ) { 
    print $_ . "\n";
}

#print Dumper $array_ref; エラーが発生する事なく正常に配列を出力
----array---- 
0
1
2
配列をリファレンスだけで定義する

スカラーのリファレンスを利用して@による配列自体をそのまま定義するのではなく、リファレンス型だけで定義する事ができます。リファレンス型で定義するには[]という無名配列生成子を利用します。

#!/usr/local/bin/perl -w

use Data::Dumper;

print "----array---- \n";

my $array_ref = [ 0, 1, 2 ];
foreach( @{$array_ref} ) { 
    print $_ . "\n";
}

#print Dumper $array_ref;
----array---- 
0
1
2
連想配列をシンプルに定義する

連想配列の定義には%を利用します。パターン1か3が一般的な記述で、パターン2のkey/valueをカンマで区切るのは可読性が落ちると思います。foreachで回す場合はkeysにより連想配列のkeyを取得します。取得したkeyは自動的に特殊変数$_に展開され、値はスカラーを使い$hash{$_}のように参照します。keyの値はデフォルトではsortされていないので、昇順にする場合はsort関数を利用します。

#!/usr/local/bin/perl -w

print "----hash---- \n";
# パターン1
my %hash = ( key0 => 0, key1 => 1, key2 => 2 );
# パターン2
my %hash = ( key0, 0, key1, 1, key2, 2 );
# パターン3
my %hash;
$hash{key0} = 0;
$hash{key1} = 1;
$hash{key2} = 2;

foreach( sort keys %hash ) {
    print "Key = $_  Value = $hash{$_} \n";
}
----hash---- 
Key = key0  Value = 0 
Key = key1  Value = 1 
Key = key2  Value = 2 
連想配列のリファレンス定義

配列と同様にリファレンスを定義します。連想配列のリファレンスを参照するには\%hashと定義します。デリファレンスにする場合は%{$hash_ref}とします。リファレンスから値を参照する時にはアラー演算子(->)を利用します。

#!/usr/local/bin/perl -w

print "----hash---- \n";
my %hash = ( key0 => 0, key1 => 1, key2 => 2 );
my $hash_ref = \%hash;

foreach( sort keys %{$hash_ref} ) {
    print "Key = $_  Value = $hash_ref->{$_} \n";
}
----hash---- 
Key = key0  Value = 0 
Key = key1  Value = 1 
Key = key2  Value = 2 
連想配列をリファレンスだけで定義する

配列と同様にリファレンスだけで連想配列を定義することができます。連想配列をリファレンスだけで定義する場合は無名ハッシュ生成子({})を利用します。

#!/usr/local/bin/perl -w

print "----hash---- \n";
my $hash_ref = { key0 => 0, key1 => 1, key2 => 2 };
foreach( sort keys %{$hash_ref} ) {
    print "Key = $_  Value = $hash_ref->{$_} \n";
}

配列/連想配列テクニック

連想配列の中に配列を追加する

連想配列を定義して特定のkey以下に配列を追加したい場合は配列のpush関数を利用します。注意としてpush関数の第一引数は配列を指定する必要があるため、@{%hash{key}}のように配列形式で定義します。連想配列をpush関数の第一引数として渡すと当然エラーになります。連想配列の中に単純に配列を定義したい場合は$hash{key} = [@array];のように定義する事もできますが、この記述では配列要素の再追加はできません。再追加にはpushが必要になります。

#!/usr/local/bin/perl -w

my %hash = ();
$hash{key} = ();
my @array = (1,2,3);

#配列を連想配列に追加
push( @{$hash{key}}, @array );

#更に配列を追加
my @array2 = (4,5,6);
#下を定義すると前の要素が消えてしまう。よって再追加するにはpushが必要。
#$hash{key} = [ @array2 ];
push( @{$hash{key}}, @array2 );

foreach my $key ( sort keys %hash ) {
    print "Key = $key \n";
    foreach( @{$hash{$key}} ) {
        print "Value = $_ \n";
    }
}
Key = key 
Value = 1 
Value = 2 
Value = 3 
Value = 4 
Value = 5 
Value = 6 
配列の要素を検索する

配列に含まれている要素を検索するには一般的にはgrepを利用します。grep以外の方法としてはhashのkeyに配列の値を展開してdefinedでチェックする方法があります。連想配列の場合も同様にgrepを使ってkeyや値が含まれているかを検索します。連想配列ではkeysやvaluesを使ってそれぞれの配列を取得する必要があります。

#!/usr/local/bin/perl -w

my @array = (1,2,3,4,5,6,7,8,9);
if( grep{ $_ == 5 } @array ) {
    print "exists 5 in array \n";
}

if( grep{ $_ == 10 } @array ) {
    print "exists 10 in array \n";
}

my %hash;
$hash{$_} = 1 for @array;

if ( defined $hash{5} ) {
    print "exists 5 in array \n";
}

if ( defined $hash{10} ) {
    print "exists 10 in array \n";
}
exists 5 in array 
exists 5 in array 
#!/usr/local/bin/perl -w

my %hash = ( key0 => 'value0', key1 => 'value1', key2 => 'value2' );

if( grep{ $_ eq 'key1' } keys %hash ) {
    print "exists key1 in hash \n";
}

if( grep{ $_ eq 'value1' } values %hash ) {
    print "exists value1 in hash \n";
}

if( grep{ $_ eq 'key3' } keys %hash ) {
    print "exists key3 in hash \n";
}

if( grep{ $_ eq 'value3' } values %hash ) {
    print "exists value3 in hash \n";
}
exists key1 in hash 
exists value1 in hash
配列から重複要素を削除

配列から重複要素を削除する場合にもgrep連想配列を利用します。連想配列のkeyに配列の値、連想配列の値に配列中の個数を代入していきます。連想配列中の値の個数が0であれば配列に格納するという手段で重複値が削除されます。下に例を示しますがこのコードを自分で思いつくのは至難の業であり、Perlの黒魔術的な性質を感じさせますが、使い方さえ覚えてしまえばとても便利だと言えます。

#!/usr/local/bin/perl -w

my @array = ( 'perl', 'php', 'java', 'java', 'php' );
my %count;
@array = grep { !$count{$_}++ } @array;
foreach( @array ) { 
    print $_ . "\n"
}
perl
php
java