NAME

perlfaq4 - データ操作


DESCRIPTION

FAQのこのセクションでは、数値、日付、文字列、配列、ハッシュその他の データの取り扱いに関する質問に回答しています。


データ: 数

なぜ 19.95 のような数字ではなく、19.9499999999999 のような長い数字が出てきたんでしょうか?

内部的には、あなたの使っているコンピュータは浮動小数点数を 2 進数を 使って表現しています。 (2 のべき乗のような) デジタルなコンピュータは全ての数値を正確に 保管することはできません。 実数は処理中に精度が落ちることがあります。 これはコンピュータがどのように数値を保管するかの問題で、Perl だけではなく 全てのコンピュータ言語に影響を与えます。

perlnumber には、数値表現と変換に関する不愉快な詳細が記されています。

10 進数の桁数を制限するには、printf や sprintf の関数が使えます。 更なる詳細については "Floating Point Arithmetic" を参照してください。

        printf "%.2f", 10/3;
        my $number = sprintf "%.2f", 10/3;

なぜ int() は壊れているのでしょう?

int() はほぼ確実に正しく動作しています。 これは、数値というものがあなたの考えているものと違うからです。

まず、"Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)?" に対する答えを参照してください。

例えば、これは:

        print int(0.6/0.2-2), "\n";

ほとんどのコンピュータでは 1 ではなく 0 を表示します; 0.6 や 0.2 と言った単純な数値であっても、浮動小数点数で正確に表現できません。 さきほどあなたが "3" と考えたものは、実際には 2.9999999999999995559 と いったものです。

なぜ私の 8 進データは正しく解釈されないのでしょうか?

(brian d foy によって寄贈されました)

おそらく文字列を数値に変換しようとしているのでしょうが、Perl は 10 進数しか変換しません。 Perl が文字列を数値に変換するとき、先頭の空白とゼロは無視して、残りの 数字は 10 進数であると仮定します:

        my $string = '0644';
        print $string + 0;  # prints 644
        print $string + 44; # prints 688, certainly not octal!

この問題は普通コマンドライン引数として 8 進数を使う同じ名前の Unix コマンドがある Perl 組み込み関数に関わります。 この例では、コマンドラインの chmod は、最初の引数として 8 進数を 求めているので、最初の引数が 8 進数だと分かっています:

        %prompt> chmod 644 file

もし同じリテラルの数字 (644) を Perl で使いたいなら、 先頭に 0 を付けるか oct を使うことで Perl にこれを 8 進数で 扱うように教える必要があります:

        chmod(     0644, $file);   # right, has leading zero
        chmod( oct(644), $file );  # also correct

この問題は、Perl が文字列として考えているところ、例えば @ARGV のコマンドライン引数から数字を持ってくる場合に起こります:

        chmod( $ARGV[0],      $file);   # wrong, even if "0644"
        chmod( oct($ARGV[0]), $file );  # correct, treat string as octal

使っている値があなたの考えている形と一致しているかを確認するために、 いつでも値を 8 進表記で表示することでチェックできます。 8 進数と 10 進数で表示します:

        printf "0%o %d", $number, $number;

Perl には丸め関数がありますか? ceil()floor() とは何ですか? 三角関数は?

int() は 0 へ向かって丸めを行うことを思い出してください。 特定の桁数で丸めを行うには、sprintf()printf() を使うことが 通常はもっとも簡単なやり方です。

        printf("%.3f", 3.1415926535);   # prints 3.142

(標準 Perl 配布キットの一部である)POSIX モジュールは ceil()floor()、そしてその他の数学的な関数や三角関数の多くを実装しています。

        use POSIX;
        $ceil   = ceil(3.5);   # 4
        $floor  = floor(3.5);  # 3

perl の 5.000 から 5.003 では、三角関数は Math::Complex モジュールの中で 実行されていました。 5.004 では、Math::Trig モジュール(標準 Perl 配布キットの一部です)が 三角関数を実装しています。 内部的にはこれは Math::Complex を使っていて、一部の関数は実数値を複素数領域へ 変化させることができます。 2 の inverse sine がその一例です。

金融に関係するアプリケーションにおいては、丸めはきちんとした実装を 必要とするかもしれません。 そして、丸めの方法は適切に使われるべきものです。 この場合、Perl が使っているシステムによる丸めを信用すべきではなく、 自分自身で丸め関数を実装するようにすべきでしょう。

なぜかを見るために、中間点反復に関する問題があるということに注意しましょう:

        for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}
        0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
        0.8 0.8 0.9 0.9 1.0 1.0

Perl を責めないでください。 これはCでも同じことなのです。 IEEE ではこのようにすることを述べています。 Perl での数値は絶対値で 2**31(32 ビットマシンの場合)以下の場合の整数値であれば 数学的な整数と同じように振る舞います。 それ以外の数値は恩恵を受けません。

数値表現や基底や基数を変換するには?

Perl ではいつものことですが、これを行うには複数の方法があります。 以下は、一般的な数値表現の変換を行うための手法のいくつかの例です。 これは完全性よりも説明性を意図しています。

perlfaq4 の後の方での例では CPAN にある Bit::Vector を使っています。 perl 組み込みの関数よりも Bit::Vector を選択する理由は、 どんな大きさの数でも動作し、いくつかの操作では速度のために最適化されていて、 少なくともいくらかのプログラマにとっては表記がわかりやすいからです。

16 進数を 10 進数に変換するには?

0x 表記による perl の組み込み変換を使って:

        $dec = 0xDEADBEEF;

hex 関数を使って:

        $dec = hex("DEADBEEF");

pack を使って:

        $dec = unpack("N", pack("H8", substr("0" x 8 . "DEADBEEF", -8)));

CPAN の Bit::Vector モジュールを使って:

        use Bit::Vector;
        $vec = Bit::Vector->new_Hex(32, "DEADBEEF");
        $dec = $vec->to_Dec();
10 進数を 16 進数に変換するには?

sprintf を使って:

        $hex = sprintf("%X", 3735928559); # upper case A-F
        $hex = sprintf("%x", 3735928559); # lower case a-f

unpack を使って:

        $hex = unpack("H*", pack("N", 3735928559));

Bit::Vector を使って:

        use Bit::Vector;
        $vec = Bit::Vector->new_Dec(32, -559038737);
        $hex = $vec->to_Hex();

そして Bit::Vector は半端なビット数にも対応しています:

        use Bit::Vector;
        $vec = Bit::Vector->new_Dec(33, 3735928559);
        $vec->Resize(32); # suppress leading 0 if unwanted
        $hex = $vec->to_Hex();
8 進数を 10 進数に変換するには?

先頭に 0 を付けることによる Perl 組み込みの変換を使って:

        $dec = 033653337357; # note the leading 0!

oct function 関数を使って:

        $dec = oct("33653337357");

Bit::Vector を使って:

        use Bit::Vector;
        $vec = Bit::Vector->new(32);
        $vec->Chunk_List_Store(3, split(//, reverse "33653337357"));
        $dec = $vec->to_Dec();
10 進数を 8 進数に変換するには?

sprintf を使って:

        $oct = sprintf("%o", 3735928559);

Bit::Vector を使って:

        use Bit::Vector;
        $vec = Bit::Vector->new_Dec(32, -559038737);
        $oct = reverse join('', $vec->Chunk_List_Read(3));
2 進数から 10 進数に変換するには?

Perl 5.6 から、0b 表記を使って直接 2 進数を書くことができます:

        $number = 0b10110110;

oct を使って:

        my $input = "10110110";
        $decimal = oct( "0b$input" );

packord を使って:

        $decimal = ord(pack('B8', '10110110'));

より大きな文字列に対しては、packunpack を使って:

        $int = unpack("N", pack("B32",
        substr("0" x 32 . "11110101011011011111011101111", -32)));
        $dec = sprintf("%d", $int);
        # substr() is used to left pad a 32 character string with zeros.

Bit::Vector を使って:

        $vec = Bit::Vector->new_Bin(32, "11011110101011011011111011101111");
        $dec = $vec->to_Dec();
How do I convert from decimal to binary

sprintf を使って(perl 5.6 以降):

        $bin = sprintf("%b", 3735928559);

unpack を使って:

        $bin = unpack("B*", pack("N", 3735928559));

Bit::Vector を使って:

        use Bit::Vector;
        $vec = Bit::Vector->new_Dec(32, -559038737);
        $bin = $vec->to_Bin();

残りの変換 (16 進 -> 8 進、2 進 -> 16 進、など) は読者への宿題として 残しておきます。

なぜ & は私の思った通りに動作しないのでしょうか?

バイナリ算術演算子の振る舞いはそれが数値に対して使われているのか 文字列に対して使われているかということに依存しています。 その演算子は文字列をビットの並びとして扱います("3" という文字列は 00110011 というビットパターンとなります)。 この演算子はバイナリ形式に対して働きます (3 という数値は 00000011 というビットパターンとして扱われます)。

ですから、11 & 3 は数値に対する "and" として働きます(結果は 3 です)。 "11" & "3"は文字列に対する "and" として働きます(結果は "1" です)。

ありがちな問題は &| を使ったときに、プログラマは オペランドが数値と考えているのに実際は文字列であるようなときに 起こります。 例を挙げましょう:

        if ("\020\020" & "\101\101") {
                # ...
                }

この場合の結果は二つのナルバイトを含む文字列となります ("\020\020" の結果です)が、これは Perl における偽の値では ありません。 以下のようにする必要があります:

        if ( ("\020\020" & "\101\101") !~ /[^\000]/) {
                # ...
                }

行列の積を計算するには?

Math::Matrix モジュールか、Math::MatrixReal モジュール(CPAN で入手できます)か PDL エクステンション(これも CPAN で入手できます)を使います。

整数値の並びに対してある操作を実行するには?

配列の各要素に対して関数を呼び出して、結果を集めるにはこうします:

        @results = map { my_func($_) } @array;

例えば:

        @triple = map { 3 * $_ } @single;

配列の各要素に対して関数を呼び出すけれども、結果を無視するという 場合にはこうします:

        foreach $iterator (@array) {
                some_func($iterator);
                }

ある(小さな)範囲にある整数に対して関数を呼び出すには、こうも できます:

        @results = map { some_func($_) } (5 .. 25);

ただし、.. 演算子がその範囲にあるすべての整数の配列を生成するということに 注意すべきでしょう。 これによって大きな範囲を使った場合に大量のメモリを消費することになります。 代わりにこうします:

        @results = ();
        for ($i=5; $i < 500_005; $i++) {
                push(@results, some_func($i));
                }

この状況は Perl5.005 で修正されました。 for ループで .. を使うことで、 範囲全体を生成することなく特定の範囲の繰り返しを行えます。

        for my $i (5 .. 500_005) {
                push(@results, some_func($i));
                }

このようにしても 500,000 個の整数のリストが生成されたりはしません。

ローマ数字を出力するには?

http://www.cpan.org/modules/by-module/Roman モジュールを入手しましょう。

なぜ私の乱数はランダムでないの?

5.004 より前のバージョンの Perl を使っているなら、srand を プログラムの開始時点で一度呼び出してやって、乱数生成器の種を セットしてやらなければなりません。

         BEGIN { srand() if $] < 5.004 }

5.004 以降のものでは開始時点で自動的に srand を呼び出します。 二度以上 srand を呼び出してはいけません。 乱数の質を落としてしまいます。

コンピュータは予測できる物事に関しては役に立ちますが、ランダムな ことに対してはそうではありません(それはあなたのプログラム自身のバグによって 引き起こされることですが:-) Tom Phoenix がこの問題について語っている、 http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz の "Far More Than You Ever Wanted To Know" の中の random という記事を 参照してください。 ジョン・フォン・ノイマン曰く、「決定論的手段によって 乱数を作ろうと試みる全ての人はもちろん罪深きものである。」

randsrand が提供するものよりもよりランダムな数値が必要なら、 CPAN にある Math::TrulyRandom モジュールも チェックしてみると良いでしょう。 これはあなたの使っているシステムのタイマーを乱数を生成するのに 使っていて不完全な面もありますが、十分なものです。 あなたの使うオペレーティングシステムで使えるものよりも もっと良質な擬似乱数を必要としているのなら、 http://www.nr.com にある ``Numerical Recipes in C'' を見るとよいでしょう。

X と Y の間の乱数を得るには?

二つの値の間の乱数を得るためには、まず 0 と 1 との間の乱数を得るために rand() 組み込み関数を使います。 それから、これを必要な範囲にシフトします。

rand($x)0 <= rand($x) < $x という値を返します。 従って、perl に作ってほしいものは、0 から、必要な XY との差 までの範囲の乱数です。

つまり、10 から 15 の範囲の値(両端を含む) を得るためには、 0 から 5 の範囲の乱数を求めて、それに 10 を加えます。

        my $number = 10 + int rand( 15-10+1 ); # ( 10,11,12,13,14, or 15 )

従って、これを抽象化するために以下のサンプル関数を導き出します。 これは与えられた二つの整数を含む範囲のランダムな整数を選択します。 例えば: random_int_between(50,120)

        sub random_int_between {
                my($min, $max) = @_;
                # Assumes that the two arguments are integers themselves!
                return $min if $min == $max;
                ($min, $max) = ($max, $min)  if  $min > $max;
                return $min + int rand(1 + $max - $min);
                }


データ:日付

その年の何日目であるかを知るには?

localtime 関数はその年の何日目であるかを返します。 引数なしの localtime は現在時刻を使います。

        $day_of_year = (localtime)[7];

POSIX モジュールも日付をその年の何日目か、または何週目かに整形します。

        use POSIX qw/strftime/;
        my $day_of_year  = strftime "%j", localtime;
        my $week_of_year = strftime "%W", localtime;

任意の日付に対してその年の何日目かを得るには、localtime の引数から 紀元からの秒数を求めるために、POSIXmktime を使います。

        use POSIX qw/mktime strftime/;
        my $week_of_year = strftime "%W",
                localtime( mktime( 0, 0, 0, 18, 11, 87 ) );

Date::Calc モジュールはこれらを計算する二つの関数を提供します。

        use Date::Calc;
        my $day_of_year  = Day_of_Year(  1987, 12, 18 );
        my $week_of_year = Week_of_Year( 1987, 12, 18 );

現在の世紀や千年紀を知るには?

以下の単純な関数を使ってください:

        sub get_century    {
                return int((((localtime(shift || time))[5] + 1999))/100);
                }
        sub get_millennium {
                return 1+int((((localtime(shift || time))[5] + 1899))/1000);
                }

システムによっては、POSIX モジュールの strftime() 関数が 非標準の方法で %C フォーマット("century"だと主張されることがあります)を 使うように拡張されているかもしれません。 これは世紀ではありません。 なぜならこのようなシステムのほとんどでは、 これは 4 桁の年の上位 2 桁を示しているだけなので、 現在の世紀や千年紀を決定する信頼できる方法ではありません。

二つの日付文字列を比較するには?

(brian d foy によって寄贈されました)

日付を単に数値として保管して、それから引き算することもできます。 しかし、人生はいつもこんな風に単純とは限りません。 フォーマットされた日付に対して作業したい場合は、 Date::Manip, Date::Calc, DateTime といったモジュールが 助けになるかもしれません。

文字列を受け取って、それを紀元からの経過秒数に変換するには?

もしそれが常に同じ書式である十分に標準的な文字列であれば、それを分割して、 その部分部分を標準の Time::Local モジュールの timelocal に渡せます。 さもなければ、CPAN にある Date::Calc モジュールと Date::Manip モジュールを見るべきでしょう。

ユリウス日を求めるには?

(brian d foy と Dave Cross によって寄贈されました)

CPAN にある Time::JulianDay モジュールが使えます。 しかし、本当にユリウス日がほしいのか確認してください; 多くの人々がユリウス日に関して異なる考え方を持っています。 例としては、http://www.hermetic.ch/cal_stud/jdn.htm を参照してください。

日付・時刻をユリウス日に変換できる、DateTime モジュールを試すことも できます。

        $ perl -MDateTime -le'print DateTime->today->jd'
        2453401.5

あるいは準ユリウス日にも変換できます:

        $ perl -MDateTime -le'print DateTime->today->mjd'
        53401

あるいは年の何日目か(これがユリウス日だと考える人もいます)にも変換できます:

        $ perl -MDateTime -le'print DateTime->today->doy'
        31

How do I find yesterday's date?

(brian d foy によって寄贈されました)

Date モジュールの一つを使いましょう。 DateTime モジュールは単純で、前日の同じ時刻を返します。

        use DateTime;
        my $yesterday = DateTime->now->subtract( days => 1 );
        print "Yesterday was $yesterday\n";

Date::Calc モジュールの Today_and_Now 関数を使うこともできます。

        use Date::Calc qw( Today_and_Now Add_Delta_DHMS );
        my @date_time = Add_Delta_DHMS( Today_and_Now(), -1, 0, 0, 0 );
        print "@date_time\n";

ほとんどの人は日付を計算するのにカレンダーではなく時刻を使おうとしますが、 これは 1 日が 24 時間であることを仮定しています。 ほとんどの人々にとって、そうではない日が 2 日あります: 夏時間が始まる日と終わる日はこれを狂わせます。 この作業はモジュールにさせましょう。

もし完全に自分でしなければならない (あるいはこれらのモジュールが 使えない) 場合は、以下が Perl と共に配布されている Time::Local を 使った解法です:

        # contributed by Gunnar Hjalmarsson
         use Time::Local;
         my $today = timelocal 0, 0, 12, ( localtime )[3..5];
         my ($d, $m, $y) = ( localtime $today-86400 )[3..5];
         printf "Yesterday: %d-%02d-%02d\n", $y+1900, $m+1, $d;

この場合、正午から始まる日数を計算して、24 時間を引いています。 たとえばカレンダー日の長さが 23 時間や 25 時間でも、結局 カレンダーの前日を得られますが、この場合正午ではありません。 時刻については気にしないので、1 時間のずれは関係なく、最終的に 前日を得られます。

Perl には 2000 年問題があるのですか? Perl は 2000 年対応ですか?

短い答: いいえ、Perl には 2000 年問題はありません。 はい、Perl は(どのような意味でも) 2000 年対応です。 ただし、あなたの雇っているプログラマがそうでないように 使っているなら 2000 年問題はあります。

長い答: この質問は物事の理解を誤っています。 Perl はあなたの鉛筆と同じぐらいに Y2K 対応です。 それ以上でもそれ以下でもありません。 あなたの鉛筆を使って Y2K 対応でないメモを書けますか? もちろん書けます。 それは鉛筆のせいですか? もちろん違います。

Perl に組み込みの日付・時刻関数(gmtimeとlocaltime)は 2000 年を越えた年も区別するために必要な情報を提供しています (32 ビットマシンをトラブルが直撃するのは2038年です)。 これらの関数がリストコンテキストで使われたときに返す年数は 実際の年から 1900 を引いた値です。1910 年から 1999 年は このやり方では たまたま 二桁の数値となります。 2000 年問題を避けるには、年を二桁で扱わないようにします。

gmtime()localtime() は、スカラコンテキストで呼び出された場合には 完全な年を含んでいるタイムスタンプ文字列を返します。 たとえば、$timestamp = gmtime(1005613200) は $timestamp に "Tue Nov 13 01:00:00 2001" をセットします。 ここには 2000 年問題はありません。

このことは、Perl で 2000 年問題を起こすようなプログラムを作るのに 使えないということではありません。あなたの使う鉛筆も そうであるように。つまり、言語にまつわるミスではなく、使う人の 間違いであるということです。 NRA を刺激するかもしれませんが、 「Perl は 2000 年問題を打ち破らない。人が打ち破るのだ。」ということです。 詳しい説明は http://www.perl.org/about/y2k.html を参照してください。


データ: 文字列

入力を検査するには?

(brian d foy によって寄贈されました)

値があなたの予測している、または受け入れたいものであることを保証するには 多くの方法があります。 perlfaq でカバーする特定の例の他に、名前に "Assert" や "Validate" がある モジュールや、Regexp::Common のようなその他のモジュールを 見ることもできます。

Business::ISBN, Business::CreditCard, Email::Valid, Data::Validate::IP のように、特定の種類の入力を検査するための モジュールもあります。

文字列のアンエスケープ (unescape)をするには?

それはあなたのいう「エスケープ」がなんであるかによります。 URL のエスケープは perlfaq9 で扱っています。 バックスラッシュによるシェルエスケープは以下のようにして取り除きます:

        s/\\(.)/$1/g;

これは \n だとか \t、あるいはその他の特殊なエスケープを展開しません。

キャラクタの連続した組を取り除くには?

(brian d foy によって寄贈されました)

文字の組(または文字の並び)を探して、それを一つの実体に置き換えるには 置換演算子が使えます。 この置換で、(.) で一文字が見付かります。 記憶用のかっこはマッチングした文字を後方参照 \1 に保管し、 同じ文字を直後に要求するために使います。 文字列の一部を $1 にある文字で置き換えます。

        s/(.)\1/$1/g;

文字変換演算子 tr/// も使えます。 この例では、tr/// の検索リスト側は何も入っていませんが、c オプションが ついているので全てが含まれます。 置き換えリスト側にも何も入っていないので、文字変換はほとんど何もしません (より厳密には、文字はその文字自身に置き換えられます)。 しかし、s オプションは文字列中の重複していて連続した文字を 1 文字に 短縮するので、次に同じ文字がある文字は表示されません:

        my $str = 'Haarlem';   # in the Netherlands
        $str =~ tr///cs;       # Now Harlem, like in New York

文字列中にある関数呼び出しを展開するには?

(brian d foy によって寄贈されました)

これは perlref に文書化されていて、もっとも読みやすいものでは ありませんが、動きます。 これらの例のそれぞれにおいて、大かっこの内側の関数はリファレンスを デリファレンスするために呼び出します。 もし複数の返り値がある場合、無名配列を構築して、デリファレンスします。 この場合、関数をリストコンテキストで呼び出します。

        print "The time values are @{ [localtime] }.\n";

スカラコンテキストで関数を呼び出したい場合、もう少し作業が必要です。 実際に置きたいどんなコードでも中かっこの中に置けるので、 (それをどのようにするかはあなた次第で、中かっこのなかのコードを使えますが) 単にスカラリファレンスで終了する必要があります。 かっこはリストコンテキストを作成するので、関数内でスカラコンテキストを 強制するために scalar が必要であることに注意してください:

        print "The time is ${\(scalar localtime)}.\n"
        print "The time is ${ my $x = localtime; \$x }.\n";

関数がすでにリファレンスを返す場合、自分でリファレンスを作る必要は ありません。

        sub timestamp { my $t = localtime; \$t }
        print "The time is ${ timestamp() }.\n";

Interpolation モジュールもまたあなたのために多くの魔法を使います。 展開を行う tie されたハッシュを設定するための変数名(この場合は E)を 指定できます。 同じようにこれを行うその他のいくつかのメソッドを持っています。

        use Interpolation E => 'eval';
        print "The time values are $E{localtime()}.\n";

ほとんどの場合、文字列連結を使ってスカラコンテキストに強制するほうが おそらくより簡単です。

        print "The time is " . localtime() . ".\n";

何かがマッチしている/ネストしているということを検出するには?

これは一つの正規表現で解決できないほどの複雑な問題なのです。 単一のキャラクター二つに囲まれた何かを見つけだすには、 /x([^x]*)x/ といったパターンを使えば $1 に検査の結果が得られるでしょう。 複数キャラクターに囲まれたものの場合は、 /alpha(.*?)omega/ のようなパターンが必要となるでしょう。 しかし、ネストしたパターンを扱うようなものはありませんし、できません。 (, {, [, < のいずれかのバランス表現をデリミタとして 使っている場合、CPAN にある Regexp::Common モジュールを使うか、 (??{ code }) in perlre を参照してください。 その他の場合では、パーサーを書く必要があります。

もしまじめにパーザを作ろうと考えているのなら、 それを手助けしてくれるようなモジュールやその他のプログラムがあります。 CPAN には Parse::RecDescent, Parse::Yapp, Text::Balanced がありますし、byacc プログラムもあります。 perl 5.8 から、Text::Balanced は標準配布の一部になりました。

単純で破壊的な inside-out アプローチもあります。 これは以下のようにして一度に最小のネスト部分を取り出そうというものです。

        while (s/BEGIN((?:(?!BEGIN)(?!END).)*)END//gs) {
                # do something with $1
                }

より複雑で巧妙なやり方に Perl の正規表現エンジンを使うというものがあります。 これは Dean Inada によるもので Obfuscated Perl コンテストに エントリされるような代物ですが、正しく働きます:

        # $_ には解析対象の文字列があります
        # BEGINとENDはネストしたテキストの開始と終了とを行います。
        @( = ('(','');
        @) = (')','');
        ($re=$_)=~s/((BEGIN)|(END)|.)/$)[!$3]\Q$1\E$([!$2]/gs;
        @$ = (eval{/$re/},$@!~/unmatched/i);
        print join("\n",@$[0..$#$]) if( $$[-1] );

文字列をひっくり返すには?

reverse in perlfunc で説明されているように、スカラコンテキストで reverse() を使います。

        $reversed = reverse $string;

文字列中にあるタブを展開するには?

以下のようにしてできます:

        1 while $string =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;

あるいは、ただ単に Text::Tabs モジュール(標準 Perl 配布キットの一部です)を 使ってもできます。

        use Text::Tabs;
        @expanded_lines = expand(@lines_with_tabs);

段落を整形するには?

Text::Wrap (標準 Perl 配布キットの一部です)を使います。

        use Text::Wrap;
        print wrap("\t", '  ', @paragraphs);

Text::Wrap に与える段落には埋め込みの改行があってはいけません。 Text::Wrap は行を均等割り付けしません(左寄せします)。

または CPAN の Text::Autoformat モジュールを使ってください。 ファイルの整形は以下のようにシェルエイリアスを作ることによって 簡単に実現できます:

        alias fmt="perl -i -MText::Autoformat -n0777 \
                -e 'print autoformat $_, {all=>1}' $*"

その多くの能力を評価するために、Text::Autoformat の文書を 参照してください。

文字列の最初の N 文字にアクセスしたり、それを変更するには?

文字列の先頭の文字へは substr() でアクセスできます。 例えば、最初の文字を得るには、位置 0 から始めて、長さ 1 の文字列を 取得します。

        $string = "Just another Perl Hacker";
        $first_char = substr( $string, 0, 1 );  #  'J'

文字列の一部を変換するために、省略可能な 4 番目の引数として置き換える 文字列を指定できます。

        substr( $string, 13, 4, "Perl 5.8.0" );

substr() を左辺値として使うこともできます。

        substr( $string, 13, 4 ) =  "Perl 5.8.0";

何かの N 番目のものを変更するには?

自分で N 番目の記録を取る必要があります。 例えば、(大小文字の違いを無視して) 5 番目に現れた "whoever""whomever""whosoever""whomsoever" に変更したいと考えているとしましょう。 以下は全て $_ に変更したい文字列が入っているものとします。

        $count = 0;
        s{((whom?)ever)}{
        ++$count == 5       # is it the 5th?
            ? "${2}soever"  # yes, swap
            : $1            # renege and leave it there
                }ige;

もっと一般的なケースでは、while ループの中で /g 修飾子を使ってマッチの数を数えることもできます。

        $WANT = 3;
        $count = 0;
        $_ = "One fish two fish red fish blue fish";
        while (/(\w+)\s+fish\b/gi) {
                if (++$count == $WANT) {
                        print "The third fish is a $1 one.\n";
                        }
                }

これは "The third fish is a red one." のように出力します。 以下のようにパターンの繰り返し回数を指定するやり方もあります:

        /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i;

ある文字列の中に存在する部分文字列が何個あるのかを数えるには?

様々な効率を持った、いろいろなやり方があります。 文字列中に存在しているある単一キャラクター (X) の数を数えたいのであれば、 tr/// 関数を使って次のようにできます:

        $string = "ThisXlineXhasXsomeXx'sXinXit";
        $count = ($string =~ tr/X//);
        print "There are $count X characters in the string";

これは単一キャラクターを対象にするのであればちょうどいいものですが、 大きな文字列中の、複数キャラクターから構成される部分文字列の数を 数えようとしても、tr/// はうまく動作しません。 ここで可能なのは、グローバルなパターンマッチを while() で囲んでしまうという ものです。たとえば、負の数を数えるのならこうします:

        $string = "-9 55 48 -2 23 -76 4 14 -44";
        while ($string =~ /-\d+/g) { $count++ }
        print "There are $count negative numbers in the string";

もう一つのバージョンでは、リストコンテキストでグローバルマッチングを 使って、その結果をスカラに代入することで、マッチングした数を数えます。

        $count = () = $string =~ /-\d+/g;

Perl には 2038 年問題はありますか?

いいえ、Perl 組み込みの日付時刻関数とモジュールの全ては 1970 年の 前後約 20 億年に対して正しく動作します。

多くのシステムでは 2038 年以降の時刻を数えられません。 古いバージョンの Perl では日付の計算を行うためにシステムに依存していたので、 システムの 2038 年バグを共有していました。

一行にあるすべての単語をキャピタライズするには?

(brian d foy によって寄贈されました)

Damian Conway による Text::Autoformat は、考えるべき全てのことを 処理してくれます:

        use Text::Autoformat;
        my $x = "Dr. Strangelove or: How I Learned to Stop ".
          "Worrying and Love the Bomb";
        print $x, "\n";
        for my $style (qw( sentence title highlight )) {
                print autoformat($x, { case => $style }), "\n";
                }

これらの単語のキャピタライズはどうしたいですか?

        FRED AND BARNEY'S LODGE        # all uppercase
        Fred And Barney's Lodge        # title case
        Fred and Barney's Lodge        # highlight case

これは見た目ほど簡単な問題ではありません。 これらにはいくつの単語があると思いますか? 考え中…考え中…。 もし 5 と答えたなら、あなたは正しいです。 Perl での単語は \w+ の集合ですが、これはあなたが キャピタライズしたいものではありません。 アポストロフィの後の s はキャピタライズしないように Perl に 知らせるには? 正規表現に挑戦してみましょう:

        $string =~ s/ (
                                 (^\w)    #at the beginning of the line
                                   |      # or
                                 (\s\w)   #preceded by whitespace
                                   )
                                /\U$1/xg;
        $string =~ s/([\w']+)/\u\L$1/g;

ここで、"and" をキャピタライズしないようにするには? Text::Autoformat を使って、次の問題に取り組んでください :)

(とある文字)の内側にある時を除き、(とある文字)で終端されている文字列を分割するには?

いくつかのモジュールがこのようなパースを扱います-- Text::Balanced, Text::CSV, Text::CSV_XS, Text::ParseWords などです。

カンマで分割された文字列を別々のフィールドに置くような例を 考えてみましょう。 ここで split(/,/) を使うことはできません。 なぜなら、クォートの内側にあるカンマで分割すべきではないからです。 例えば以下のようなデータを考えてみましょう。

        SAR001,"","Cimetrix, Inc","Bob Smith","CAM",N,8,1,0,7,"Error, Core Dumped"

クォートの制約のためにこれは実に複雑な問題です。 ありがたいことに、私たちには Mastering Regular Expressions の著者でもあり、 この問題を私たちのために扱ってくれる Jeffrey Friedl がいます。 彼の提案はこうです(文字列が $text にあると仮定しています):

         @new = ();
         push(@new, $+) while $text =~ m{
                 "([^\"\\]*(?:\\.[^\"\\]*)*)",?  # groups the phrase inside the quotes
                | ([^,]+),?
                | ,
                }gx;
         push(@new, undef) if substr($text,-1,1) eq ',';

クォーテーションマークで終端されたフィールドの中で クォーテーションマークを表現したいのならば、 それをバックスラッシュで("like \"this\"" のように)エスケープしてください。

あるいは、Text::PaserWords モジュール(標準 Perl 配布の一部です)を 使ってこうします:

        use Text::ParseWords;
        @new = quotewords(",", 0, $text);

文字列の先頭や末尾にある空白を剥ぎ取るには?

(brian d foy によって寄贈されました)

置換を使うことで行えます。 1 行では、先頭か末尾のどちらかの空白全てを削除します。 2 つの置換でこれが行えます。

        s/^\s+//;
        s/\s+$//;

これを 1 回の置換で書くこともできますが、組み合わせた文は分かれている 文よりも遅くなります。 しかし、それはあなたには問題がないかもしれません。

        s/^\s+|\s+$//g;

この正規表現において、アンカーは the alternation より低い優先順位を 持つので、the alternation は文字列の先頭か末尾にマッチングします。 /g フラグが付いていることにより、可能な全てのマッチングについて置換が 行われるので、先頭と末尾の両方で行われます。 引き続く改行は \s+ にマッチングし、$ アンカーは文字列の物理的な 末尾にマッチングするので、改行も消えることを忘れないでください。 単に出力に改行を追加することで、^\s+ がすべて削除してしまう「空の」 (空白だけからなる)行を保存する利点が追加されます。

        while( <> )
                {
                s/^\s+|\s+$//g;
                print "$_\n";
                }

複数行の文字列に対しては、/m ("multi-line") フラグを追加することにより、 文字列中の論理行毎に正規表現を適用できます。 /m フラグをつけると、$ 組み込まれた改行の 前に マッチングするので、 これを取り除けません。 文字列の末尾の改行は取り除けます。

        $string =~ s/^\s+|\s+$//gm;

空白だけからなる文字列は、置換の最初の部分が文字列全体にマッチングして、 それを空文字列に置き換えてしまうので、消えてしまうことに注意してください。 もし組み込まれている空行を保存したいなら、さらにもう少し作業をする必要が あります。 全ての空白(改行を含みます)にマッチングさせる代わりに、単にその他の 空白にマッチングさせます。

        $string =~ s/^[\t\f ]+|[\t\f ]+$//mg;

文字列に空白をパッディングしたり、数値にゼロをパッディングしたりするには?

以下に挙げる例で、$pad_len はパッディングしたい文字列の長さです。 $text$num は文字列にパッディングの対象となる内容を保持していて、 $pad_char がパッディングに使いたいキャラクターを保持しています。 やっていることがわかっているのなら、$pad_char という変数の代わりに一 文字のキャラクター文字列を使うこともできます。 そして同様に、パッディングしたい長さが予め分かっているなら、 $pad_len に整数値を指定することも出来ます。

最も単純なやり方は sprintf 関数を使うというものです。 この関数は文字列の左や右にパッディングを行ったり、0 を左に置いたりする ことができます。 pack 関数は文字列の右側に空白でパッディングすることと、 結果の最大長を $pad_len に切り詰めることだけができます。

        # Left padding a string with blanks (no truncation):
        $padded = sprintf("%${pad_len}s", $text);
        $padded = sprintf("%*s", $pad_len, $text);  # same thing
        # Right padding a string with blanks (no truncation):
        $padded = sprintf("%-${pad_len}s", $text);
        $padded = sprintf("%-*s", $pad_len, $text); # same thing
        # Left padding a number with 0 (no truncation):
        $padded = sprintf("%0${pad_len}d", $num);
        $padded = sprintf("%0*d", $pad_len, $num); # same thing
        # Right padding a string with blanks using pack (will truncate):
        $padded = pack("A$pad_len",$text);

空白やゼロ以外のキャラクターでパッディングを行いたいのであれば、 以下に挙げるやり方を使うことができます。これらは全て パッディング文字列を x 修飾子で生成して $text と結合します。 これらのメソッドは $text を切り詰めません。

任意のキャラクターによる左詰めと右詰めを行い、新しい文字列を作ります:

        $padded = $pad_char x ( $pad_len - length( $text ) ) . $text;
        $padded = $text . $pad_char x ( $pad_len - length( $text ) );

任意のキャラクターによる左詰めと右詰めを行い、$text を直接変更します:

        substr( $text, 0, 0 ) = $pad_char x ( $pad_len - length( $text ) );
        $text .= $pad_char x ( $pad_len - length( $text ) );

文字列から選択されたカラムを取り出すには?

(brian d foy によって寄贈されました)

データが含まれている桁が分かっているなら、単一の桁を展開するために substr が使えます。

        my $column = substr( $line, $start_column, $length );

桁が空白やその他のデリミタで分けられていて、データの一部としては 空白やデリミタが現れないなら、split が使えます。

        my $line    = ' fred barney   betty   ';
        my @columns = split /\s+/, $line;
                # ( '', 'fred', 'barney', 'betty' );
        my $line    = 'fred||barney||betty';
        my @columns = split /\|/, $line;
                # ( 'fred', '', 'barney', '', 'betty' );

カンマ区切りの値(CSV)を扱いたい場合は、フォーマットが少し複雑なので これはしないで下さい。 Text::CSV, Text::CSV_XS, Text::CSV_PP のような、この フォーマットを扱うためのモジュールの一つを使ってください。

固定桁の行全体を分解したいなら、unpack の A (ASCII) フォーマットが 使えます。 フォーマット指定子の後に数値をつけることで、桁数を指定できます。 更なる詳細については perlfuncpackunpack の項目を 参照してください。

        my @fields = unpack( $line, "A8 A8 A8 A16 A4" );

unpack のフォーマット引数での空白はリテラルな空白を意味しないことに 注意してください。 もし空白で区切られたデータがあるなら、代わりに split を使ったほうが いいかもしれません。

文字列の soundex 値を見つけるには?

(brian d foy によって寄贈されました)

Text::Soundex モジュールが使えます。 あいまいマッチングや近傍マッチングを行いたいなら、 String::Approx, Text::Metaphone, Text::DoubleMetaphone といった モジュールを試すのも良いでしょう。

テキスト文字列の中にある変数を展開するには?

(brian d foy によって寄贈されました)

もし避けることが可能なら、してはいけません; あるいは、Text::TemplateTemplate ツールキットのような テンプレートシステムが使えるなら、これらを代わりに使ってください。 sprintfprintf を使って作業をこなすことすらできます:

        my $string = sprintf 'Say hello to %s and %s', $foo, $bar;

しかし、完全なテンプレートシステムを引っ張り出したくないような一度限りの 簡単な場合には、内部に二つの Perl スカラ変数を持つ文字列を使います。 この例では、$foo$bar をその変数の値に展開したいとします:

        my $foo = 'Fred';
        my $bar = 'Barney';
        $string = 'Say hello to $foo and $bar';

これを行う一つの方法は、置換演算子と二つの /e フラグを使うものです。 一つ目の /e は置き換え側の $1 を評価して $foo に変えます。 二つ目の /e$foo をその値に変えます。 従って、$foo は 'Fred' に変わって、それが結局最終的に文字列に 残されるものになります:

        $string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'

/e は暗黙のうちに struct 違反を無視するので、未定義の変数名を 空文字列に置き換えます。 /e フラグを (2 回も!) 使っているので、eval を文字列の形で使うのと 同じセキュリティ問題を全て抱えています。 もし $foo に(おそらく @{[ system "rm -rf /" ]} のような)変なものが 入っていたら、トラブルに出会うことになります。

セキュリティ問題を避けるために、変数名を評価するのではなくハッシュから 値を取ってくることもできます。 /e を一つ使って、ハッシュに値があることを確認し、もしなければ、 値をマーカーに置き換えます; この場合は、??? が何かがおかしいことの 印です;

        my $string = 'This has $foo and $bar';
        my %Replacements = (
                foo  => 'Fred',
                );
        # $string =~ s/\$(\w+)/$Replacements{$1}/g;
        $string =~ s/\$(\w+)/
                exists $Replacements{$1} ? $Replacements{$1} : '???'
                /eg;
        print $string;

常にクォーティング "$vars" することの何が悪いの?

そういったダブルクォートが、強制的に文字列化(stringification)するのが問題で、 たとえそれを望んでいなくても数値やリファレンスが強制的に 文字列に変換されてしまうのです。 このように考えましょう: ダブルクォートは新しい文字列を生成するのに使われる。 もしあなたがすでに文字列を持っているのであれば、使う必要が あるでしょうか?

以下の例のような変な書き方をすると:

        print "$var";           # BAD
        $new = "$old";          # BAD
        somefunc("$var");       # BAD

あなたはトラブルに巻き込まれることになるでしょう。 これらは(99.8% は)、より単純、かつより直接的に書くべきなのです。

        print $var;
        $new = $old;
        somefunc($var);

さもなければ、プログラムを遅くなることのほかにも、スカラが実際には文字列でも 数値でもなくリファレンスであるようなときにあなたのプログラムが おかしくなることになります。

        func(\@array);
        sub func {
                my $aref = shift;
                my $oref = "$aref";  # WRONG
                }

マジカル ++ オートインクリメント演算子や syscall() 関数のような、 文字列と数値の間の違いを実際に気にするような Perl の幾つかの操作において、 微妙な問題に直面するかもしれません。

文字列化(stringfication)も配列を壊します。

        @lines = `command`;
        print "@lines";     # WRONG - extra blanks
        print @lines;       # right

なぜ私の <<HERE ドキュメントがうまく動かないのでしょう?

以下の三つの点を確認してください。

<< パートの後ろに空白があってはいけません。
終端にセミコロンが置かれているかもしれません。
タグの前に任意のスペースを置くことはできません。

ヒアドキュメントのテキストでインデントを使いたいのであれば、 以下のようにしてできます:

    # all in one
    ($VAR = <<HERE_TARGET) =~ s/^\s+//gm;
        your text
        goes here
    HERE_TARGET

しかしこの場合も HERE_TARGET は先頭に置かなければなりません。 もしこれもインデントしたいのなら、インデントをクォートする必要があるでしょう。

    ($quote = <<'    FINIS') =~ s/^\s+//gm;
            ...we will have peace, when you and all your works have
            perished--and the works of your dark master to whom you
            would deliver us. You are a liar, Saruman, and a corrupter
            of men's hearts.  --Theoden in /usr/src/perl/taint.c
        FINIS
    $quote =~ s/\s+--/\n--/;

以下はインデントされたヒアドキュメントのための汎用 fixer-upper 関数です。 この関数は引数にヒアドキュメントを渡されることを期待しています。 これは共通の部分文字列で始まる各行について、 その部分文字列を剥ぎ取るということを行います。 あるいは、最初の行の先頭にある空白を取り、 続く行に対しても同じ様に削除を行います。

    sub fix {
        local $_ = shift;
        my ($white, $leader);  # common whitespace and common leading string
        if (/^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/) {
            ($white, $leader) = ($2, quotemeta($1));
        } else {
            ($white, $leader) = (/^(\s+)/, '');
        }
        s/^\s*?$leader(?:$white)?//gm;
        return $_;
    }

この関数は先頭にある特別な、動的に決められる文字列に対しても使えます:

        $remember_the_main = fix<<'    MAIN_INTERPRETER_LOOP';
        @@@ int
        @@@ runops() {
        @@@     SAVEI32(runlevel);
        @@@     runlevel++;
        @@@     while ( op = (*op->op_ppaddr)() );
        @@@     TAINT_NOT;
        @@@     return 0;
        @@@ }
        MAIN_INTERPRETER_LOOP

また、先頭にある特定の個数の空白を取り除いて、インデントを 正しく残すようなこともできます:

        $poem = fix<<EVER_ON_AND_ON;
       Now far ahead the Road has gone,
          And I must follow, if I can,
       Pursuing it with eager feet,
          Until it joins some larger way
       Where many paths and errands meet.
          And whither then? I cannot say.
                --Bilbo in /usr/src/perl/pp_ctl.c
        EVER_ON_AND_ON


データ: 配列

リストと配列の差とはなんですか?

配列は長さを変えることができます。リストはできません。 ある配列に対して push や pop ができますが、 リストに対しては値のセットしかできません。 一部には配列が変数であるのに対してリストは値であると 区別して考えている人達もいます。 リストを受け取ったり返したりするサブルーチンはリストコンテキストに あなたを導き、リストで配列を初期化したり、foreach() でリストを辿ったりできます。 @変数は配列であり、無名配列も配列です。スカラコンテキストの配列は その要素数のように振る舞います。 サブルーチンはその引数を @_ という配列を通してアクセスし、 push/pop/shift は配列に対してのみ働きます。

スカラコンテキストでリストとして振る舞うものはないということに 注意してください。 以下のようにすると:

        $scalar = (2, 5, 7, 9);

スカラコンテキストでカンマ演算子を使ったものであり、 スカラカンマ演算子を使います。 これはリストでもなんでもありません! この結果は最後の値である 9 となります。

$array[1] と @array[1] との間の違いはなんですか?

前者はスカラ値であり、後者は一つのスカラ値を持ったリストを構成する 配列のスライスです。スカラ値を必要とするならば(ほとんど の場合がこうでしょう)$を使うべきで、@ は一つのスカラ値を持った リストを必要とするとき(実際のところ、この状況は非常に希でしょう) に使うべきものです。

これらはあるときには違いがありませんが、違いがでる場合もあります。 例えば以下のものを:

        $good[0] = `some program that outputs several lines`;

次のものと比較する場合です:

        @bad[0]  = `same program that outputs several lines`;

use warnings プラグマと -w フラグはこのことに関する警告を行います。

配列やリストにある重複した要素を削除するには?

(brian d foy によって寄贈されました)

ハッシュを使ってください。 「ユニーク」や「重複」といった単語を考えたときには、 「ハッシュキー」を考えてください。

要素の順番を気にしないなら、単にハッシュを作ってキーを取り出してください。 どのようにハッシュを作るかは重要ではありません: 単にユニークな要素を取り出すために keys を使うためです。

        my %hash   = map { $_, 1 } @array;
        # or a hash slice: @hash{ @array } = ();
        # or a foreach: $hash{$_} = 1 foreach ( @array );
        my @unique = keys %hash;

モジュールを使いたいなら、List::MoreUtilsuniq 関数を 試してみてください。 リストコンテキストでは、リストの順序を保存した形でユニークな要素を返します。 スカラコンテキストでは、ユニークな要素の数を返します。

        use List::MoreUtils qw(uniq);
        my @unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 1,2,3,4,5,6,7
        my $unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 7

各要素を調べて、一度見つけたものをスキップすることもできます。 記録をつけるためにハッシュを使います。 ループが最初に要素を見つけると、この要素は %Seen にキーがありません。 next 文はキーを作り、直ちにその値(undef)を使うので、 ループは push を続行し、このキーの値をインクリメントします。 次回にループが同じ要素を見つけると、ハッシュにはそのキーが存在し、 かつ そのキーの値が(0 でも undef でもないので)真なので、 next は反復をスキップし、ループは次の要素に進みます。

        my @unique = ();
        my %seen   = ();
        foreach my $elem ( @array )
                {
                next if $seen{ $elem }++;
                push @unique, $elem;
                }

grep を使うことでより簡単に書くこともでき、これは同じことになります。

        my %seen = ();
        my @unique = grep { ! $seen{ $_ }++ } @array;

リストや配列の内容にある特定の要素があるかどうかを確かめるには?

(この回答の一部は Anno Siegel と brian d foy によって寄贈されました)

ハッシュはこの質問に対する速くて効率の良い解答のために デザインされています。 配列はそうではありません。

幾つかのやり方があります。 Perl 5.10 以降では、アイテムが配列やハッシュに含まれているかを チェックするのにスマートマッチング演算子が使えます:

        use 5.010;
        if( $item ~~ @array )
                {
                say "The array contains $item"
                }
        if( $item ~~ %hash )
                {
                say "The hash contains $item"
                }

以前のバージョンの Perl では、もう少し作業が必要です。 この問い合わせを多くのアイテムに対して 行いたいとか、値が任意の文字列である場合には最も速いやり方は元の 配列の逆のものを作って元の配列の値をキーとするようなハッシュを 管理するというものです:

        @blues = qw/azure cerulean teal turquoise lapis-lazuli/;
        %is_blue = ();
        for (@blues) { $is_blue{$_} = 1 }

こうすれば、$is_blue{$some_color} がどうであるかでチェックできます。 最初の場所で bules にハッシュのすべてを保持させるのはよい考えでしょう。

値のすべてが小さな整数であれば、単純な添え字付き配列を使うことができます。 この種の配列はより少ない場所しか使いません。

        @primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
        @is_tiny_prime = ();
        for (@primes) { $is_tiny_prime[$_] = 1 }
        # or simply  @istiny_prime[@primes] = (1) x @primes;

これで $is_tiny_prime[$some_number] の内容がどうであるかで チェックできます。

問い合わせる値が文字列ではなく整数であるのならば、ビットストリングを 使うことによって大幅に空間を節約することができます。

        @articles = ( 1..10, 150..2000, 2017 );
        undef $read;
        for (@articles) { vec($read,$_,1) = 1 }

これで vec($read,$n,1) が真かどうかで $n の検査ができます。

これらのメソッドは個々のテストの速さが保証されていますが、 元のリストや配列の再構成が必要です。 同じ配列に対して複数の値をテストする必要がある場合にのみ元が取れます。

一度だけテストする場合、標準モジュール List::Util がこの目的のために first 関数をエクスポートしています。 これは要素を見つけると停止することで動作します。 速度のために C で書かれていて、これの Perl の等価な処理は、次のサブルーチンの ようになります:

        sub first (&@) {
                my $code = shift;
                foreach (@_) {
                        return $_ if &{$code}();
                }
                undef;
        }

速度が問題ではないなら、一般的な方法は、リスト全体をトラバースするために grep をスカラコンテキストで使う(これで条件をパスしたアイテムの数を返します) ことです。 しかし、これには何回マッチングしたのかを知らせるという利点があります。

        my $is_there = grep $_ eq $whatever, @array;

実際にマッチングした要素を展開したい場合は、単にリストコンテキストで grep を使ってください。

        my @matches = grep $_ eq $whatever, @array;

二つの配列の差(difference)を求めるには? 二つの配列の共通要素(inter section)を求めるには?

ハッシュを使います。 以下のプログラム片は質問の両方を行います。 与えられた配列の要素には重複がないと仮定しています。

        @union = @intersection = @difference = ();
        %count = ();
        foreach $element (@array1, @array2) { $count{$element}++ }
        foreach $element (keys %count) {
                push @union, $element;
                push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
                }

これは 対称的差分、つまり、A か B のどちらかにあって、 両方にあることはない全ての要素である、ということに注意してください。 これは xor 操作のようなものと考えてください。

二つの配列や二つのハッシュが等しいかどうかを検査するには?

Perl 5.10 以降では、スマートマッチング演算子が最小の作業で答えを 与えてくれます:

        use 5.010;
        if( @array1 ~~ @array2 )
                {
                say "The arrays are the same";
                }
        if( %hash1 ~~ %hash2 ) # doesn't check values!
                {
                say "The hash keys are the same";
                }

以下に挙げる例は一レベルの配列に対して有効です。 これは文字列としての比較を使い、定義済みと未定義の空文字列を区別しません。 必要に応じて修正してください。

        $are_equal = compare_arrays(\@frogs, \@toads);
        sub compare_arrays {
                my ($first, $second) = @_;
                no warnings;  # silence spurious -w undef complaints
                return 0 unless @$first == @$second;
                for (my $i = 0; $i < @$first; $i++) {
                        return 0 if $first->[$i] ne $second->[$i];
                        }
                return 1;
                }

多重レベル構造に対応するために、あなたは以下のような手段を使いたいと 考えるかもしれません。 ここでは CPAN モジュールの FreezeThaw を使っています:

        use FreezeThaw qw(cmpStr);
        @a = @b = ( "this", "that", [ "more", "stuff" ] );
        printf "a and b contain %s arrays\n",
                cmpStr(\@a, \@b) == 0
                ? "the same"
                : "different";

このアプローチはハッシュの比較にも使えます。 以下に二種類の回答をお見せしましょう:

        use FreezeThaw qw(cmpStr cmpStrHard);
        %a = %b = ( "this" => "that", "extra" => [ "more", "stuff" ] );
        $a{EXTRA} = \%b;
        $b{EXTRA} = \%a;
        printf "a and b contain %s hashes\n",
        cmpStr(\%a, \%b) == 0 ? "the same" : "different";
        printf "a and b contain %s hashes\n",
        cmpStrHard(\%a, \%b) == 0 ? "the same" : "different";

最初のものは二つのハッシュが同じ内容であると報告しますが、二番目の ものは違うと報告します。

ある条件が真となる最初の配列要素を見つけだすには?

条件を満たす最初の配列要素を探すためには、Perl 5.8 から同梱されている List::Util モジュールの first() 関数が使えます。 この例は "Perl" を含む最初の要素を探します。

        use List::Util qw(first);
        my $element = first { /Perl/ } @array;

List::Util が使えない場合は、同じことをするために自分でループを書きます。 要素が見つかったら、last を使ってループを停止します。

        my $found;
        foreach ( @array ) {
                if( /Perl/ ) { $found = $_; last }
                }

配列の添え字がほしい場合は、添え字に順番に変えていって、 それぞれの添え字の配列要素をチェックして、条件を満たすものが 見つかるまで繰り返します。

        my( $found, $index ) = ( undef, -1 );
        for( $i = 0; $i < @array; $i++ ) {
                if( $array[$i] =~ /Perl/ ) {
                        $found = $array[$i];
                        $index = $i;
                        last;
                        }
                }

リンク付きリストを扱うには?

一般的には、Perl ではリンク付きリストを扱う必要はありません。 なぜなら、通常の配列を使って push や pop、shift や unsift を使って両端で 操作できたり、splice を使って任意の場所にある任意個の要素を加えたり 削除したりすることができるからです。 pop と shift は両方ともが、Perl の動的配列に対する O(1) の操作です。 shift や pop がなかった場合、push は一般的には log(N) 回毎のオーダーで 再割り当てが必要になります。 そして unshift は呼ばれる毎にポインターのコピーが必要になるでしょう。

もし、本当に、本当にリンク付きリストを使いたいのなら、perldscperltoot で説明されているようなデータ構造を使うことができ、 アルゴリズムの教科書にあるようなことができます。 例えば以下のようなリストノードをを考えてみましょう:

        $node = {
                VALUE => 42,
                LINK  => undef,
                };

リストを渡り歩くには以下のようにします:

        print "List: ";
        for ($node = $head;  $node; $node = $node->{LINK}) {
                print $node->{VALUE}, " ";
                }
        print "\n";

以下のやり方でリストに追加できます:

        my ($head, $tail);
        $tail = append($head, 1);       # grow a new head
        for $value ( 2 .. 10 ) {
                $tail = append($tail, $value);
                }
        sub append {
                my($list, $value) = @_;
                my $node = { VALUE => $value };
                if ($list) {
                        $node->{LINK} = $list->{LINK};
                        $list->{LINK} = $node;
                        }
                else {
                        $_[0] = $node;      # replace caller's version
                        }
                return $node;
                }

しかし繰り返しますが、Perl の組み込み型は事実上常に充分なものなのです。

How do I handle circular lists?

(brian d foy によって寄贈されました)

配列を無限に循環したいなら、配列の添え字をインクリメントして 要素数の剰余を取ったものを使います:

        my @array = qw( a b c );
        my $i = 0;
        while( 1 ) {
                print $array[ $i++ % @array ], "\n";
                last if $i > 20;
                }

常に循環配列の次の要素をもつスカラを使うための、 Tie::Cycle を使うこともできます:

        use Tie::Cycle;
        tie my $cycle, 'Tie::Cycle', [ qw( FFFFFF 000000 FFFF00 ) ];
        print $cycle; # FFFFFF
        print $cycle; # 000000
        print $cycle; # FFFF00

Array::Iterator::Circular は循環配列のための反復子オブジェクトを 作ります:

        use Array::Iterator::Circular;
        my $color_iterator = Array::Iterator::Circular->new(
                qw(red green blue orange)
                );
        foreach ( 1 .. 20 ) {
                print $color_iterator->next, "\n";
                }

配列をランダムにかき混ぜるには?

Perl 5.8.0 以降がインストールされているか、Scalar-List-Utils 1.03 以降が インストールされているなら、以下のように出来ます:

        use List::Util 'shuffle';
        @shuffled = shuffle(@list);

そうでないなら、Fisher-Yates 法が使えます。

        sub fisher_yates_shuffle {
                my $deck = shift;  # $deck is a reference to an array
                return unless @$deck; # must not be empty!
                my $i = @$deck;
                while (--$i) {
                        my $j = int rand ($i+1);
                        @$deck[$i,$j] = @$deck[$j,$i];
                        }
        }
        # shuffle my mpeg collection
        #
        my @mpeg = <audio/*/*.mp3>;
        fisher_yates_shuffle( \@mpeg );    # randomize @mpeg in place
        print @mpeg;

List::Util::shuffle() はリストを受け取って、混ぜられた新しいリストを 返しますが、上記の実装は配列そのものを混ぜることに注意してください。

splice を使ったシャッフルアルゴリズムを見たことがあるかもしれません。 カレントの要素をランダムに取り出した別の要素と交換します:

        srand;
        @new = ();
        @old = 1 .. 10;  # just a demo
        while (@old) {
                push(@new, splice(@old, rand @old, 1));
                }

これは splice が O(N) であり、さらにそれを N 回呼んでいるのですから 良くありません。 つまりこれは O(N**2) のアルゴリズムです。 これは大きな配列に使わなければあなたはその効率の悪さに気がつかないでしょう。

配列の各要素に対する処理や、変更を行うには?

for/foreach を使います:

        for (@lines) {
                s/foo/bar/;     # change that word
                tr/XZ/ZX/;      # swap those letters
                }

別の方法です; 球の体積を求めます:

        for (@volumes = @radii) {   # @volumes has changed parts
                $_ **= 3;
                $_ *= (4/3) * 3.14159;  # this will be constant folded
                }

リストを他のリストに変換する map() を使っても行えます:

        @volumes = map {$_ ** 3 * (4/3) * 3.14159} @radii;

同じことをハッシュの値に対して行いたいのであれば、values は使えません。 Perl 5.6 以降では値はコピーされないので、(この場合では) $orbit を変更すると、 値を変更することになります。

        for $orbit ( values %orbits ) {
                ($orbit **= 3) *= (4/3) * 3.14159;
                }

perl 5.6 以前では、values は値のコピーを返すので、古い perl の コードでは、ハッシュを修正しているところで values %orbits ではなく @orbits{keys %orbits} と書いていることがよくあります。

ある配列からランダムに要素を選択するには?

rand() 関数を使います(rand in perlfunc を参照):

        $index   = rand @array;
        $element = $array[$index];

あるいは、単純に:

        my $element = $array[ rand @array ];

How do I permute N elements of a list?

CPAN にある List::Permutor モジュールを使ってください。 リストが実際には配列なら、Algorithm::Permute モジュール(これも CPAN に あります)を試してください。 これは XS コードで書かれていて、とても効率的です:

        use Algorithm::Permute;
        my @array = 'a'..'d';
        my $p_iterator = Algorithm::Permute->new ( \@array );
        while (my @perm = $p_iterator->next) {
           print "next permutation: (@perm)\n";
                }

より速い実行のために、以下のようにも出来ます:

        use Algorithm::Permute;
        my @array = 'a'..'d';
        Algorithm::Permute::permute {
                print "next permutation: (@array)\n";
                } @array;

以下の小さなプログラムは入力された行にある各単語の順列をすべて生成します。 関数 permute() で使われているアルゴリズムは Knuth の The Art of Computer Programming の Volume 4 (未発行) で 議論されていて、任意のリストで動作するはずです:

        #!/usr/bin/perl -n
        # Fischer-Krause ordered permutation generator
        sub permute (&@) {
                my $code = shift;
                my @idx = 0..$#_;
                while ( $code->(@_[@idx]) ) {
                        my $p = $#idx;
                        --$p while $idx[$p-1] > $idx[$p];
                        my $q = $p or return;
                        push @idx, reverse splice @idx, $p;
                        ++$q while $idx[$p-1] > $idx[$q];
                        @idx[$p-1,$q]=@idx[$q,$p-1];
                }
        }
        permute { print "@_\n" } split;

Algorithm::Loops モジュールも the NextPermuteNextPermuteNum の 関数を提供していて、重複した値が含まれていても、その場で変更して、配列の 全てのユニークな順列を探します: もしその要素が逆順にソートされているなら、配列を反転させて、ソートを 行い、偽を返します; さもなければ次の順列を返します。

NextPermute は文字列順を使い、NextPermuteNum は数値順を使うので、 0..9 の全ての順番を数え上げるには以下のようにします:

        use Algorithm::Loops qw(NextPermuteNum);
    my @list= 0..9;
    do { print "@list\n" } while NextPermuteNum @list;

(なにか)で配列をソートするには?

sort() (sort in perlfunc に説明があります)のための比較関数を作ります:

        @list = sort { $a <=> $b } @list;

デフォルトのソート関数は文字列比較である cmp で、(1, 2, 10)(1, 10, 2) に並び変えます。 上の例では、数値比較演算子である <=> を使っています。

ソートするものの一部を取り出す必要があるような複雑な関数を使うのなら、 ソート関数の内側でそれを使ってはいけません。 最初にその関数で使う部分を取り出します。 なぜなら、sort BLOCK は同じ要素に対して何度も何度も呼び出される 可能性があるからです。 以下の例は、各アイテムの最初の番号の後にある最初の単語を取り出し、 その後でそれらの単語を大小文字を無視してソートします。

        @idx = ();
        for (@data) {
                ($item) = /\d+\s*(\S+)/;
                push @idx, uc($item);
            }
        @sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];

これはシュワルツ変換と呼ばれるトリックを使って以下のように 書くこともできます:

        @sorted = map  { $_->[0] }
                sort { $a->[1] cmp $b->[1] }
                map  { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;

幾つかのフィールドを使ってソートする必要があるのなら、 以下のやり方が便利でしょう。

        @sorted = sort {
                field1($a) <=> field1($b) ||
                field2($a) cmp field2($b) ||
                field3($a) cmp field3($b)
                } @data;

これは先の例にあったキーの precalculation と組み合わせることも できます。

この手法に関する更なる情報については http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz にある "Far More Than You Ever Wanted To Know" コレクションの sort という記事を 参照してください。

perlfaq4 で後述する、ハッシュのソートに関する質問も参照してください。

ビット配列を扱うには?

pack()unpack() か、vec() とビット演算を使います。

例えば、それぞれのビットを配列に格納する(これをすると多くのメモリが 無駄になります)必要はありません。 ビットの配列を文字列に変換するには、正しいビットをセットするのに vec() 使います。 これは、$ints[N] がセットされている場合のみ $vec の bit N を セットします。

        @ints = (...); # array of bits, e.g. ( 1, 0, 0, 1, 1, 0 ... )
        $vec = '';
        foreach( 0 .. $#ints ) {
                vec($vec,$_,1) = 1 if $ints[$_];
                }

文字列 $vec は必要なビット数だけを取ります。 例えば、@ints に 16 エントリがあるとすると、$vec はこれを 格納するのに (スカラ変数のオーバーヘッドを除いて) 2 バイトだけを 使います。

次に挙げる例は、$vec で与えられるベクターのビットを配列 @ints に 取り出すものです:

        sub bitvec_to_list {
                my $vec = shift;
                my @ints;
                # Find null-byte density then select best algorithm
                if ($vec =~ tr/\0// / length $vec > 0.95) {
                        use integer;
                        my $i;
                        # This method is faster with mostly null-bytes
                        while($vec =~ /[^\0]/g ) {
                                $i = -9 + 8 * pos $vec;
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                push @ints, $i if vec($vec, ++$i, 1);
                                }
                        }
                else {
                        # This method is a fast general algorithm
                        use integer;
                        my $bits = unpack "b*", $vec;
                        push @ints, 0 if $bits =~ s/^(\d)// && $1;
                        push @ints, pos $bits while($bits =~ /1/g);
                        }
                return \@ints;
                }

この方法はビットベクターが疎であるときにさらに高速になります (Tim Bunce と Winfried Koenig によるものです)。

Benjamin Goldberg の提案を使って、while ループをはるかに 短くすることもできます:

        while($vec =~ /[^\0]+/g ) {
                push @ints, grep vec($vec, $_, 1), $-[0] * 8 .. $+[0] * 8;
                }

または CPAN の Bit::Vector モジュールを使います:

        $vector = Bit::Vector->new($num_of_bits);
        $vector->Index_List_Store(@ints);
        @ints = $vector->Index_List_Read();

Bit::Vector は、ビットベクタ、小さい整数の就業、「大きな整数」の 計算に関する効果的なメソッドを提供します。

以下は、vec() を使った広範囲な説明です:

        # vec demo
        $vector = "\xff\x0f\xef\xfe";
        print "Ilya's string \\xff\\x0f\\xef\\xfe represents the number ",
        unpack("N", $vector), "\n";
        $is_set = vec($vector, 23, 1);
        print "Its 23rd bit is ", $is_set ? "set" : "clear", ".\n";
        pvec($vector);
        set_vec(1,1,1);
        set_vec(3,1,1);
        set_vec(23,1,1);
        set_vec(3,1,3);
        set_vec(3,2,3);
        set_vec(3,4,3);
        set_vec(3,4,7);
        set_vec(3,8,3);
        set_vec(3,8,7);
        set_vec(0,32,17);
        set_vec(1,32,17);
        sub set_vec {
                my ($offset, $width, $value) = @_;
                my $vector = '';
                vec($vector, $offset, $width) = $value;
                print "offset=$offset width=$width value=$value\n";
                pvec($vector);
                }
        sub pvec {
                my $vector = shift;
                my $bits = unpack("b*", $vector);
                my $i = 0;
                my $BASE = 8;
                print "vector length in bytes: ", length($vector), "\n";
                @bytes = unpack("A8" x length($vector), $bits);
                print "bits are: @bytes\n\n";
                }

なぜ空の配列やハッシュに defined() を使ったときに真が返ってくるのでしょう?

簡単にいえば、スカラや関数に対してのみ defined を使うべきで、 集成体(aggregates, 配列やハッシュ)に対して使うべきではないのです。 詳しくは 5.004 以降の defined in perlfunc を参照してください。


データ: ハッシュ(連想配列)

ハッシュ全体を処理するには?

(brian d foy によって寄贈されました)

ハッシュ全体を処理するには二つの方法があります。 キーのリストを取得してからキー毎に処理するか、一度に一つのキー-値の ペアを取得するかです。

全てのキーを得るには、keys 関数を使います。 ハッシュの全てのキーを展開してリストの形で返します。 それから処理したい特定のキーの値を取得します。

        foreach my $key ( keys %hash ) {
                my $value = $hash{$key}
                ...
                }

キーのリストを取得したら、ハッシュ要素を処理する前にリストを処理できます。 例えば、キーをソートしてレキシカルな順序で処理できます:

        foreach my $key ( sort keys %hash ) {
                my $value = $hash{$key}
                ...
                }

あるいは、アイテムの一部に対してのみ処理したいかもしれません。 キーが text: で始まるキーのみ扱いたいなら、単に grep を使って 選択できます:

        foreach my $key ( grep /^text:/, keys %hash ) {
                my $value = $hash{$key}
                ...
                }

ハッシュがとても大きい場合、キーの長いリストを作りたくないかもしれません。 メモリを節約するために、まだ取得していないキー-値の組を一つ返す each() を 使って組を取得できます:

        while( my( $key, $value ) = each( %hash ) ) {
                ...
                }

each 演算子は組を一見ランダムな順序で返すので、順序が問題になる場合は、 keys メソッドを使う必要があります。

しかし、each() 演算子は少しトリッキーです。 Perl が内部的に全ての要素を再ハッシュした後にいくつかの組をスキップしたり 再処理したりすることなく、ハッシュの使用中にハッシュのキーを追加したり 削除したりすることは出来ません。 さらに、一つのハッシュは一つのイテレータしか持っていないので、もし同じ ハッシュに対して keys, values, each を使うと、イテレータを リセットしてしまって処理が無茶苦茶になってしまいます。 さらなる詳細については perlfunceach エントリを参照してください。

二つのハッシュをマージするには?

(brian d foy によって寄贈されました)

二つのハッシュをマージしようと決める前に、もし両方のハッシュに 同じキーがあったときにどうするかと、元のハッシュをそのままに しておくかどうかを決める必要があります。

もし元のハッシュを保存しておきたいなら、一つのハッシュ (%hash1) を 新しいハッシュ (%new_hash) にコピーして、それからもう一つのハッシュ (%hash2) からキーを新しいハッシュに追加します。 キーが既に %new_hash にある場合、重複をどうするかを決定する 機会が与えられます:

        my %new_hash = %hash1; # make a copy; leave %hash1 alone
        foreach my $key2 ( keys %hash2 )
                {
                if( exists $new_hash{$key2} )
                        {
                        warn "Key [$key2] is in both hashes!";
                        # handle the duplicate (perhaps only warning)
                        ...
                        next;
                        }
                else
                        {
                        $new_hash{$key2} = $hash2{$key2};
                        }
                }

新しいハッシュを作りたくない場合は、やはりこのループ技術が使えます; 単に %new_hash%hash1 に変更します。

        foreach my $key2 ( keys %hash2 )
                {
                if( exists $hash1{$key2} )
                        {
                        warn "Key [$key2] is in both hashes!";
                        # handle the duplicate (perhaps only warning)
                        ...
                        next;
                        }
                else
                        {
                        $hash1{$key2} = $hash2{$key2};
                        }
                }

片方のキーと値がもう片方で上書きされても気にしないなら、片方のハッシュから もう片方に追加するために単にハッシュスライスを使えます。 この場合、共通のキーがあった場合は、 %hash1 の値は %hash2 の値で置き換えられます:

        @hash1{ keys %hash2 } = values %hash2;

ハッシュに対して反復操作を行っているときにキーの追加や削除をすると何が起きますか?

(brian d foy によって寄贈されました)

簡単な答えは:「そんなことをするな!」

ハッシュを each() で反復すると、それについて気にすることなく、 最後に返されたキーを削除できます。 他のキーを削除または追加すると、perl はハッシュテーブルを再構成するので、 反復子はキーを読み飛ばしたり重複して読んだりするかもしれません。 perlfunceach() のエントリを参照してください。

ハッシュの要素をその値で検索するには?

リバースハッシュを作成します:

        %by_value = reverse %by_key;
        $key = $by_value{$value};

これは特に効率がよいものではありません。 空間を効率よく使うにはこうします:

        while (($key, $value) = each %by_key) {
                $by_value{$value} = $key;
            }

ハッシュに同じ値がある場合には、このメソッドは最初に見つかったキーだけを 見つけだします。 あなたはこれを気にするかも知れませんし、気にしないかもしれません。 もし気にするのなら、いつでもハッシュの代わりに配列のハッシュを 使うことができます:

        while (($key, $value) = each %by_key) {
                 push @{$key_list_by_value{$value}}, $key;
                }

ハッシュにどれくらいの要素があるのを知るには?

(brian d foy によって寄贈されました)

これは perlfaq4 の "How do I process an entire hash?" と とてもよく似ていますが、一般的な場合では少し単純になります。

ハッシュにいくつのエントリがあるかを知るために、スカラコンテキストで keys() 組み込み関数を使えます:

        my $key_count = keys %hash; # must be scalar context!

定義された値のエントリの数を知りたい場合は、少し異なります。 それぞれの値をチェックする必要があります。 grep が手軽です:

        my $defined_value_count = grep { defined } values %hash;

ある特定の条件を持つエントリを数える場合には全て同じ構造が使えます。 もしキーに母音が含まれているエントリを数えたい場合、そのように テストを置き換えるだけです:

        my $vowel_count = grep { /[aeiou]/ } keys %hash;

スカラコンテキストの grep は数を返します。 マッチングしたアイテムのリストがほしい場合は、代わりにリストコンテキストで 使うだけです:

        my @defined_values = grep { defined } values %hash;

keys() 関数も反復動作を初期化するので、もしこれを each() のような 他のハッシュ演算子を使っている間に使うと、おかしな結果になります。

ハッシュを(キーではなく値で)ソートするには?

(brian d foy によって寄贈されました)

ハッシュをソートするために、キーから始めます。 この例では、キーのリストをソート関数に渡して、それから ASCII 順で比較します (ロケール設定の影響を受けるかもしれません)。 出力リストは ASCII 順のキーのリストです。 キーを得たら、ASCII 順にキーを並べたレポートを作成します。

        my @keys = sort { $a cmp $b } keys %hash;
        foreach my $key ( @keys )
                {
                printf "%-20s %6d\n", $key, $hash{$key};
                }

しかし、sort() ブロックでもっと面白いことができます。 キーを比較する代わりに、これらの値を計算してその値を比較に使います。

例えば、大文字小文字を無視した順序のレポートを作るには、全てを小文字に するためにダブルクォートされた文字列の中で \L シーケンスが使えます。 それから sort() ブロックがキーを出力する順番を決定するために小文字化された 値を比較します。

        my @keys = sort { "\L$a" cmp "\L$b" } keys %hash;

注意: 計算が高くつくものであったり、ハッシュがたくさんの要素を持っている 場合、計算結果をキャッシュするためにシュワルツ変換を使いたいかもしれません。

もし代わりにハッシュの値でソートしたいなら、それを探すためにハッシュキーを 使います。 やはりキーのリストを使いますが、今度はその値でソートします。

        my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash;

ここから、より複雑なものにできます。 ハッシュ値が同じ場合は、ハッシュキーによる第二段階のソートを提供できます。

        my @keys = sort {
                $hash{$a} <=> $hash{$b}
                        or
                "\L$a" cmp "\L$b"
                } keys %hash;

私のハッシュを常にソートされた状態にしておくには?

DB_File/"In Memory Databases" にあるように、DB_File モジュールと tie() を使った、$DB_BTREE ハッシュ束縛を使うことができます。 CPAN の Tie::IxHash モジュールも有益かもしれません。 これによりハッシュはソートされた状態のままになりますが、tie インターフェースによって被る速度低下を気に入らないかもしれません。 あなたは本当にこれが必要ですか? :)

ハッシュに対する "delete" と "undef"との間の違いは?

ハッシュはスカラのペアからなります: 最初のスカラがキーで、 二番目のスカラが値です。 キーは文字列、数値、リファレンスのいずれの種類のスカラであっても 強制的に文字列にされます。 %hash の中に $key というキーが既にあれば、exists($hash{$key}) は 真を返します。 与えられたキーに対する値は undef とすることができます。 これは $hash{$key}undef にして、exists $hash{$key} が真を 返すという状態です。 これは ($key, undef)がハッシュに存在しているということを示しています。

図が助けになるでしょう。 以下は %hash のテーブルです:

          keys  values
        +------+------+
        |  a   |  3   |
        |  x   |  7   |
        |  d   |  0   |
        |  e   |  2   |
        +------+------+

そしてこれらが保持している状態はこうです:

        $hash{'a'}                       is true
        $hash{'d'}                       is false
        defined $hash{'d'}               is true
        defined $hash{'a'}               is true
        exists $hash{'a'}                is true (Perl 5 only)
        grep ($_ eq 'a', keys %hash)     is true

ここで

        undef $hash{'a'}

とすると、テーブルはこうなります:

          keys  values
        +------+------+
        |  a   | undef|
        |  x   |  7   |
        |  d   |  0   |
        |  e   |  2   |
        +------+------+

そしてその状態は以下のようになります。 大文字になっているのが変わった場所です:

        $hash{'a'}                       is FALSE
        $hash{'d'}                       is false
        defined $hash{'d'}               is true
        defined $hash{'a'}               is FALSE
        exists $hash{'a'}                is true (Perl 5 only)
        grep ($_ eq 'a', keys %hash)     is true

最後の二つに注目してください: あなたは undef 値を保持していますが、 キーは define されているのです!

さて、こんどは以下の例を考えてみましょう:

        delete $hash{'a'}

テーブルはこうなります:

          keys  values
        +------+------+
        |  x   |  7   |
        |  d   |  0   |
        |  e   |  2   |
        +------+------+

そしてその状態はこうです。大文字の部分が変わったところです:

        $hash{'a'}                       is false
        $hash{'d'}                       is false
        defined $hash{'d'}               is true
        defined $hash{'a'}               is false
        exists $hash{'a'}                is FALSE (Perl 5 only)
        grep ($_ eq 'a', keys %hash)     is FALSE

ほら、エントリが丸ごとなくなっていまいました!

なぜわたしの tie されたハッシュは defined と exists を区別しないのでしょうか?

これは tie されたハッシュの EXISTS() の実装に依存します。 たとえば、DBM* ファイルに tie されたハッシュには undef という考え方はありません。 これはまた DBM* ファイルにとっては exists()defined() とは同じことであり、 そういったものに対して行っていることは 通常のハッシュに対して行っていることとは違うのだということなのです。

each() 操作の途中でリセットしてしまうには?

(brian d foy によって寄贈されました)

each をリセットするために keys 関数か values 関数が使えます。 他に何もせずに単に each で使われているイテレータをリセットするには、 これらの一つを無効コンテキストで使います:

        keys %hash; # resets iterator, nothing else.
        values %hash; # resets iterator, nothing else.

perlfunc にある each の説明を参照してください。

二つのハッシュからユニークなキーを取りだすには?

まず最初にハッシュからキーを取りだして、それをリストに格納します。 そして、先に説明した「重複の削除」問題の解決を行います。例:

        %seen = ();
        for $element (keys(%foo), keys(%bar)) {
                $seen{$element}++;
                }
        @uniq = keys %seen;

あるいはもっと簡潔に:

        @uniq = keys %{{%foo,%bar}};

もし本当にメモリ空間を節約したいのなら:

        %seen = ();
        while (defined ($key = each %foo)) {
                $seen{$key}++;
        }
        while (defined ($key = each %bar)) {
                $seen{$key}++;
        }
        @uniq = keys %seen;

DBM ファイルに多次元配列を格納するには?

自分自身で構造を文字列化する(うれしくないですね)か、MLDBM モジュール (Data::Dumper を使います)を CPAN から取ってきて、DB_File か GDBM_File のいずれかのトップレイヤーにします。

わたしのハッシュが格納した順番を覚えておくようにするには?

CPAN にある Tie::IxHash を使います。

        use Tie::IxHash;
        tie my %myhash, 'Tie::IxHash';
        for (my $i=0; $i<20; $i++) {
                $myhash{$i} = 2*$i;
                }
        my @keys = keys %myhash;
        # @keys = (0,1,2,3,...)

なぜあるハッシュの未定義要素をサブルーチンに渡すとそれを作成するのでしょうか?

(brian d foy によって寄贈されました)

本当に古いバージョンの Perl を使っているのですか?

普通は、存在しないキーの値にアクセスしてもキーは作成 されません

        my %hash  = ();
        my $value = $hash{ 'foo' };
        print "This won't print\n" if exists $hash{ 'foo' };

しかし、$hash{ 'foo' } をサブルーチンに渡す場合は特別扱いでした。 $_[0] に直接代入できるので、Perl はそのような代入に備える必要があり、 そのためハッシュキーを予め作成していました:

    my_sub( $hash{ 'foo' } );
        print "This will print before 5.004\n" if exists $hash{ 'foo' };
        sub my_sub {
                # $_[0] = 'bar'; # create hash key in case you do this
                1;
                }

しかし、Perl 5.004 からは、この状況は特別扱いで、Perl は代入が 行われた場合にのみハッシュキーを作成します:

    my_sub( $hash{ 'foo' } );
        print "This will print, even after 5.004\n" if exists $hash{ 'foo' };
        sub my_sub {
                $_[0] = 'bar';
                }

しかし、昔の振る舞いにしたい(そしてこれにより奇妙な副作用が起こることについて 慎重に考えた)なら、代わりにハッシュスライスを渡せます。 Perl 5.004 はこれを特別扱いしませんでした:

        my_sub( @hash{ qw/foo/ } );

C の構造体/C++ のクラスのハッシュ、配列のハッシュ、配列と等価なものを Perl で作成するには?

通常はハッシュのリファレンスを使います。 多分以下のようになるでしょう:

        $record = {
                NAME   => "Jason",
                EMPNO  => 132,
                TITLE  => "deputy peon",
                AGE    => 23,
                SALARY => 37_000,
                PALS   => [ "Norbert", "Rhys", "Phineas"],
        };

リファレンスは perlrefperlreftut に説明があります。 複雑なデータ構造の例が perldescperllol にあります。 構造体とオブジェクト指向クラスの例が perltoot にあります。

ハッシュのキーとしてリファレンスを使うには?

(brian d foy と Ben Morrow によって寄贈されました)

ハッシュキーは文字列なので、実際にリファレンスをキーとして使うことは できません。 もしそうしようとすると、perl はリファレンスを(例えば HASH(0xDEADBEEF) の 形に)文字列化します。 少なくとも自分自身で追加の作業をしない限り、文字列化された形から リファレンスを得ることはできません。

ハッシュのエントリはリファレンスされた値がスコープ外に出ても 存在していて、その後 Perl が同じアドレスに別の変数を割り当てる可能性が あることを忘れないでください。 これは、新しい変数が偶然古い変数の値と関連づけられるかもしれないことを 意味します。

Perl 5.10 以降を使っていて、単に後で参照するためにリファレンスで 値を保管したいだけなら、 コアの Hash::Util::Fieldhash モジュールが使えます。 これはまた、もしマルチスレッドを使っている(つまり全ての変数は 新しいアドレスに再割り当てされ、文字列化した結果が変わる)場合は キーの名前の変更を扱い、リファレンスされた変数がスコープ外に 出た場合はガベージコレクションを行います。

本当にそれぞれのハッシュエントリから実際のリファレンスを得る必要があるのなら、 それに必要な作業を行う Tie::RefHash モジュールが使えます。


データ:その他

バイナリデータを正しく扱うには?

Perl はバイナリクリーンですから、バイナリデータをうまく扱えます。 しかし、 Windows や DOS では、バイナリファイルに対して行末の変換を避けるために、 binmode を使う必要があります。 一般的には、バイナリデータを扱いたいときはいつでも binmode を 使うべきです。

binmode in perlfuncperlopentut も参照してください。

もし 8 ビットテキストデータについて考えているのであれば、perllocale を 参照してください。 ただしマルチバイトキャラクターを扱いたいと考えているなら、幾つかの 罠(gotchas)があります。 正規表現のセクションを参照してください。

あるスカラ値が数値/整数/浮動小数点数かどうかを決定するには?

"NaN" や "Infinity" のような IEEE 表記については気にしないと仮定すると、 正規表現を使って行うことができます。

        if (/\D/)            { print "has nondigits\n" }
        if (/^\d+$/)         { print "is a whole number\n" }
        if (/^-?\d+$/)       { print "is an integer\n" }
        if (/^[+-]?\d+$/)    { print "is a +/- integer\n" }
        if (/^-?\d+\.?\d*$/) { print "is a real number\n" }
        if (/^-?(?:\d+(?:\.\d*)?|\.\d+)$/) { print "is a decimal number\n" }
        if (/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
                        { print "a C float\n" }

この作業のためによく使われるいくつかのモジュールがあります。 Scalar::Util (5.8 と共に配布されています) は、変数が数値に見えるか どうかを決定するために perl の内部関数 looks_like_number へのアクセスを 提供します。 Data::Types は上記のものとその他の正規表現の両方を使ってデータ型の バリデートを行う関数をエクスポートします。 3 つめとして、Regexp::Common には様々な種類の数値にマッチングする 正規表現があります。 これら 3 つのモジュールは CPAN にあります。

POSIXシステムを使っているのなら、Perlは POSIX::strtod 関数をサポートしています。 そのセマンティックは扱いにくいもので、もっと便利にアクセスするための getnum 関数を以下に例示します。 この関数は文字列を引数に取り、その文字列中で見つかった数字列に対応する 数値を返し、入力がCの小数点表記にあわないものであれば undef を返します。 is_numeric 関数は“これは数値か?”ということを知りたい場合に getnum のフロントエンドとなります。

        sub getnum {
                use POSIX qw(strtod);
                my $str = shift;
                $str =~ s/^\s+//;
                $str =~ s/\s+$//;
                $! = 0;
                my($num, $unparsed) = strtod($str);
                if (($str eq '') || ($unparsed != 0) || $!) {
                                return undef;
                        }
                else {
                        return $num;
                        }
                }
        sub is_numeric { defined getnum($_[0]) }

あるいは、CPAN にある String::Scanf モジュールをチェックしてみてください。 POSIX モジュール(標準 Perl 配布キットの一部です)は文字列から 倍精度浮動小数点数や長整数への変換を適切に行う strtodstrtol を 提供しています。

プログラムの呼び出しの間に、データ構造を永続的に保持するには?

一部の特定のアプリケーションでは、DBM モジュールの一つを使うことができます。 AnyDBM_File を参照してください。 より一般的には、CPAN にある FreezeThaw, Storable といった モジュールをあたってみるべきでしょう。 Perl 5.8 から、Storable は標準配布の一部となりました。 以下に Storablestoreretrieve を使った例を挙げます:

        use Storable;
        store(\%hash, "filename");
        # later on...
        $href = retrieve("filename");        # by ref
        %hash = %{ retrieve("filename") };   # direct to hash

再帰的なデータ構造を出力したりコピーするには?

CPAN にある Data::Dumper モジュール(5.005 以降では Perl のリリースに 含まれています)はデータ構造を出力するのに向いています。 CPAN にある Storable モジュール(5.8 以降では Perl のリリースに 含まれています)はその引数を再帰的にコピーする dclone という関数を提供しています。

        use Storable qw(dclone);
        $r2 = dclone($r1);

ここで $r1 にはあなたの望むデータ構造のリファレンスを置くことができます。 これは深くコピー(deeply copied)されます。 dclone はリファレンスを取り リファレンスを返すので、コピーしたいものが配列のハッシュであったりした 場合には余計なpunctuationが必要となるでしょう。

        %newhash = %{ dclone(\%oldhash) };

すべてのクラス/オブジェクトのためのメソッドを定義するには?

(Ben Morrow によって寄贈されました)

UNIVERSAL クラス (UNIVERSAL) を参照してください)が使えます。 しかし、これを行うことによる結果の考慮についてはとても注意深く行ってください: 全てのオブジェクトにメソッドを追加すると、想定外の結果になる可能性が とても高いです。 可能なら、あなたの作る全てのオブジェクトを共通の基底クラスから 継承するようにするか、ロールに対応している Moose のような オブジェクトシステムを使う方が良いです。

クレジットカードのチェックサムを検査するには?

CPAN から Business::CreditCard モジュールを入手してください。

XSプログラムのために倍精度実数や単精度実数の配列を pack するには?

CPAN にある PGPLOT モジュールにある arrays.h/arrays.c というものが それをします。 倍精度実数や単精度実数を大量に扱うのであれば、CPAN にある PDL モジュールを使うことを考えてみるとよいでしょう。 これは number-crunching を簡単にしてくれます。

コードについては http://search.cpan.org/dist/PGPLOT を参照してください。


REVISION

Revision: $Revision$

Date: $Date$

See perlfaq for source control details and availability.


AUTHOR AND COPYRIGHT

Copyright (c) 1997-2009 Tom Christiansen, Nathan Torkington, and other authors as noted. All rights reserved.

This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself.

Irrespective of its distribution, all code examples in this file are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit would be courteous but is not required.