2011年3月7日月曜日

Perl 5 to 6 - 一般的なPerl6データ処理イディオム

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 27 - Common Perl 6 data processing idiomの日本語訳です。

原文はCreative Commons Attribution 3.0 Germanyに基づいて公開されています。

本エントリにはCreative Commons Attribution 3.0 Unportedを適用します。

Original text: Copyright© 2008-2010 Moritz Lenz

Japanese translation: Copyright© 2011 SATOH Koichi

NAME

"Perl 5 to 6" Lesson 27 - 一般的なPerl6データ処理イディオム

SYNOPSIS

# キーと値のリストからハッシュを作る:
# 方法1: スライス
my %hash; %hash{@keys} = @values;
# 方法2: メタ演算子
my %hash = @keys Z=> @values;

# 配列の各要素に真を対応づけたハッシュを作る:
my %exists = @keys Z=> 1 xx *;

# 値を指定された範囲に制限する。ここでは範囲は 0..10
my $x = -2;
say 0 max $x min 10;

# デバッグ用: 変数の内容を変数名込みでSTDERRに書き出す
note :$x.perl;

# 大文字小文字を区別せずにソートする
say @list.sort: *.lc;

# 必須アトリビュート
class Something {
    has $.required = die "Attribute 'required' is mandatory";
}
Something.new(required => 2); # エラーなし
Something.new()               # ブーン

DESCRIPTION

ある言語で生産性を発揮するには言語仕様を学ぶだけでは不十分です。むしろ具体的な問題の解法を覚えておく必要があります。 イディオムと呼ばれる一般的な使用法のパターンは、あなたが問題に直面したときに毎回車輪の再発明をしなくとも良いように手助けしてくれます。

そこでPerl6でデータを扱う一般的なイディオムをいくつか集めました。

ハッシュ

# キーと値のリストからハッシュを作る:
# 方法1: スライス
my %hash; %hash{@keys} = @values;
# 方法2: メタ演算子
my %hash = @keys Z=> @values;

最初の方法はPerl5で使われていたものと同じです: スライスへの代入です。 2番目の方法はZip演算子Zを使っています。これはリストをジッパーのように連結します: 1, 2, 3 Z 10, 20, 301, 10, 2, 20, 3, 30になります。 Z=>はメタ演算子で、Zipを=>(ペア生成演算子)と組み合わせています。つまり1, 2, 3 Z=> 10, 20, 301 => 10, 2 => 20, 3 => 30と評価されます。これをハッシュ変数に代入するとハッシュに変換されます。

存在チェックの場合、ハッシュの値はそれが真理値コンテキストでTrueと評価されさえすれば何でも良いことがしばしばあります。 このような場合に配列あるいはキーのリストからハッシュを初期化する良い方法は

my %exists = @keys Z=> 1 xx *;

これは右辺に遅延評価される1の無限リストを使っており、Zは短い方のリストが使い切られた時点で止まることを利用しています。

数値

どこからか数値を取得し、予め決めた範囲内に収まるよう切り詰めたいことが時々あります(例えば配列の添字として使えるように)。

Perl5では結局$a = $b > $upper ? $upper : $bともう一つ下限の条件を付けることでしばしば対処してきました。 中値演算子maxminを使えば、これはかなり簡潔化できます

my $in-range = $lower max $x min $upper;

$lower max $xはどちらか大きい方の数値を返すので、範囲の下限に切り詰めることになります。

minmaxは中置演算子なので、演算子自体も切り詰められます:

$x max= 0; $x min= 10;

デバッグ

Perl5にはData::Dumperがあり、Perl6には.perlメソッドがあります。 これらは元のデータ構造を可能な限り忠実に再現するコードを生成します。

:$varはペア(「コロンペア」)を生成し、変数名をキーとして使います(ただしシジルは除きます)。つまりこれはvar => $varと同じです。 note()は標準エラーストリームにデータを改行付きで書き出します。したがってnote :$var.perlはデバッグ用に変数の値を名前と一緒に表示する手軽な手段になります。

ソート

Perl5と同様に、組み込みのsortは2引数の比較関数を取ることができ、その比較にしたがってソートを行います。 Perl5と違ってこれはちょっと賢く、関数が引数を1個しか取らない場合は自動的に変換を行ってくれます。

一般に、変換を通した値同士で比較を行いたければPerl5では次のようにできました:

# 注意: ここからPerl5コード
my @sorted = sort { transform($a) cmp transform($b) } @values;

# あるいは、いわゆるシュワルツ変換を使って:
my @sorted = map { $_->[1] }
             sort { $a->[0] cmp $b->[0] }
             map { [transform($_), $_] }
             @values

最初の方法は変換を繰り返し書く必要がある上、比較毎に変換が実行されます。 2番目の方法は変換された値を元の値と一緒に保持することでその問題を回避していますが、コード量がかなり多くなります。

Perl6は変換関数が1引数の場合、2番目の方法を自動化(し、値毎の配列生成を避けることで少しだけ効率化)します:

my @sorted = sort &transform, @values;

必須アトリビュート

アトリビュートの存在を強制する典型的な方法は、コンストラクタ内——複数存在する場合は全コンストラクタ内——で存在確認をすることです。

Perl6でもこれは動きますが、各アトリビュートのレベルで必須にする方が簡単かつ安全です:

has $.attr = die "'attr' is mandatory";

これはデフォルト値の機構を利用しています。値が与えられた場合はデフォルト値生成コードは実行されないのでdieも呼ばれません。 いずれかのコンストラクタが値をセットし損ねると例外が投げられます。

MOTIVATION

N/A

0 コメント:

コメントを投稿