ここにあるドキュメントは古いものです。 より新しいものが別ディレクトリにあります → 5.8.8ベースのperlfaq4
perlfaq4 - データ操作 ($Revision: 1.40 $, $Date: 1999/01/08 04:26:39 $)
FAQのこのセクションでは、数値、日付、文字列、配列、ハッシュその 他のデータの取り扱いに関する質問に回答しています。
なぜ19.95のような数字ではなく、19.9499999999999のような長い 数字が出てきたんでしょうか?
数学者の考える無限の実数は、 コンピューターは有限個のビットを使って無限個の数値を 収めるために、コンピューター上では近似値としてしか表現できません。
内部的には、あなたの使っているコンピューターは浮動小数点数を二進 数を使って表現しています。浮動小数点数はファイルから読み込まれた り、プログラム中にリテラルとして現れて、(19.95のような)十進浮動 小数点表記から、内部的な二進表現に変換されます。
しかし、19.95は二進の浮動小数点数では適切に表現することができま せん。これは、十進浮動小数点数で1/3を正確に表すことができないの と同じことです。
浮動小数点数が出力されるとき、二進の浮動小数点数は十進へと再度変
換されます。この十進数は、printf()で指定したフォーマットか、print
を使った場合にはカレントの出力フォーマット(perlvarを参照) で出力が行われます。Perl5での$#はPerl4とは違ったデフォルト値 を持っています。自分で$#を変更することはしないようにしてくだ さい。
これはPerlのみに限らず、二進数で十進の浮動小数点数を表すような すべてのコンピューター言語にあてはまります。Perlは任意精度の 十進数をMath::BgiFloatモジュールによって提供しています(標準Perl 配布キットの一部です)が、数学的な操作はとても遅いです。
余計な数字を取り除くには、printf("%.2f", 19.95)のように要求す る精度を取るだけの書式指定を使います。
perlopを参照してください。
なぜ私の八進データは正しく解釈されないのでしょうか?
Perlは、プログラムの中にリテラルとして現れたときにだけ八進数や十 六進数を理解します。それらのものがそれ以外の場所からとか代入で読 み込まれた場合、変換は実行されません。値の変換を必要とするのなら、 陽にoct()やへx()を使わなければなりません。oct()は八進数(``0350''や ``377''のように先頭の``0''がないものでも)と十六進数(``0x350'')の両方を 解釈するのに対して、hex()が十六進数(``0x255'', ``3A'', ``ff'', ``deadbeef'' のように、先頭に``0x''がついたりつかなかったりします)のみを変換し ます。
この問題は、パーミッションを八進数で指定するようなchmod(), mkdir(),
umask(), or
sysopen()を使おうとしたときによく発生します。
chmod(644, $file); # 間違い -- perl -w はこれを捕捉します
chmod(0644, $file); # 正しい
Perlには丸め関数がありますか? ceil()とfloor()とは何ですか?
三角関数は?
int()は0へ向かって丸めを行うことを思い出してください。
特定の桁数で丸めを行うには、sprintf()やprintf()を使うことが
通常はもっとも簡単なやり方です。
printf("%.3f", 3.1415926535); # 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が使っているシステムによる丸めを信用す べきではなく、自分自身で丸め関数を実装するようにすべきでしょう。
To see why, notice how you'll still have an issue on half-way-point alternation:
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ビットマシンの場合)以下の場合の整数値であれば数学的な整数と 同じように振る舞います。それ以外の数値は恩恵を受けません。
ビット列から整数に変換するには?
10110110のような1と0の並びを、それに対応する二進の値を 持った
スカラーに変換するには、pack()関数を 使います(説明はperlfunc
にあります)。
$decimal = pack('B8', '10110110');
別のやり方の例です:
$binary_string = join('', unpack('B*', "\x29"));
なぜ&は私の思った通りに動作しないのでしょうか?
バイナリ算術演算子の振る舞いはそれが数値に対して使われているのか
文字列に対して使われているかということに依存しています。その演算子
は文字列をビットの並びとして扱います("3"という文字列は
00110011というビットパターンとなります)。 この演算子はバイナリ形式に対して働きます
(3という数値は00000011というビットパターンとして扱われます)。
ですから、11 & 3 は数値に対する``and''として働きます(その結果は
3です)。"11" & "3"は文字列に対する``and''として働きます (結果は"1"です)。
ありがちな問題は&と|を使ったときに、プログラマーは
オペランドが数値と考えているのに実際は文字列であるようなときに
起こります。例を挙げましょう:
if ("\020\020" & "\101\101") {
# ...
}
この場合の結果は二つのナルバイトを含む文字列となります ("\020\020"の結果です)が、これはPerlにおけるfalseの値では
ありません。以下のようにする必要があります:
if ( ("\020\020" & "\101\101") !~ /[^\000]/) {
# ...
}
行列の積はどのようにやるのですか?
Math::Matrixモジュールか、Math::MatrixRealモジュール(CPANで入手 できます)か PDLエクステンション(これもCPANで入手できます)を使い ます。
整数値の並びに対してある操作を実行するには?
配列の各要素に対して関数を呼び出して、結果を集めるにはこうします:
@results = map { my_func($_) } @array;
For example: 例を挙げましょう:
@triple = map { 3 * $_ } @single;
配列の各要素に対して関数を呼び出すけれども、結果を無視するという 場合にはこうします:
foreach $iterator (@array) {
some_func($iterator);
}
ある(小さな)範囲にある整数に対して関数を呼び出すには、こうもB<できます>:
@results = map { some_func($_) } (5 .. 25);
ただし、..演算子がその範囲にあるすべての整数の配列を生成する
ということに注意すべきでしょう。これによって大きな範囲を使った場
合に大量のメモリを消費することになります。代りにこうします:
@results = ();
for ($i=5; $i < 500_005; $i++) {
push(@results, some_func($i));
}
どうすればローマ数字を出力できますか?
http://www.perl.com/CPAN/modules/by-module/Roman モジュールを 入手しましょう。
なぜ私の乱数はランダムでないの?
5.004より前のバージョンのPerlを使っているなら、srandを プログラムの開始時点で一度呼び出してやって、乱数生成器の種を
セットしてやらなければなりません。5.004以降のものでは開始時点で 自動的にsrandを呼び出します。二度以上C<srand>を呼び出しては
いけません。乱数の質を落としてしまいます。
コンピューターは予測できる物事に関しては役に立ちますが、ランダムな ことに対してはそうではありません(あなたのプログラム自身のバグ によって引き起こされることですが:-)
Tom Phoenixはこの問題について http://www.perl.com/CPAN/doc/FMTEYEWTK/random で解説しています。
ジョン・フォン・ノイマン曰く、“決定性のやり方によって 乱数を作ろうと試みる全ての人は罪にまみれて生きている”
randとsrandの組み合わせ以上のものよりも大量の乱数を必要と
しているのであれば、CPANにあるMath::TrulyRandomモジュールをチェック
してみると良いでしょう。
これはあなたの使っているシステムのタイマーを乱数を生成するのに
使っていて不完全な面もありますが、十分なものです。
あなたの使うオペレーティングシステムでつかえるもの
よりももっと良質な擬似乱数を必要としているのなら、 http://www.nr.com にある ``Numerical Recipes
in C'' を見るとよいでしょう。
その年の第何週であるとか何日目であるかを知るには?
その年での日数はlocaltime()が返す配列の中にあります (perlfuncを参照):
$day_of_year = (localtime(time()))[7];
あるいはもっと読みやすくして(5.004以降の場合):
use Time::localtime;
$day_of_year = localtime(time())->yday;
その年の第何週であるかは、これを7で割れば求められます:
$week_of_year = int($day_of_year / 7);
もちろん、ここでは数字は0から始まります。
CPANにあるDate::Calcモジュールは日数、週数などの日付計算に関する関数を多く 提供しています。
例えばアメリカのビジネスでは、しばしば 月曜を含む最初の週を第一週として考えますが、 ISO 8601では違っていて、木曜日を含む週を第一週とみなす ことに注意してください。
どうやれば二つの日付文字列を比較できますか?
もしシステム開始時点からの経過秒数で日付を格納しているのであれば、 片方からもう一方を引いてやれば求められます。 もしあなたが構造を持った日付(年、日、月、時間、分、秒を区別する) のであれば、CAPNにあるDate::ManipかDate::Calcのどちらかを 使いましょう。
どうやれば、文字列を受け取って、それをある時点からの経過秒数に変換できますか?
もしそれが常に同じ書式である十分に標準的な文字列であれば、それを
分割して、その部分部分を標準のTime::Localモジュールのtimelocalに 渡せます。さもなければ、CPANにあるDate::Calcモジュールと
Date::Manipモジュールを見るべきでしょう。
どうやればユリウス日 (またはユリウス積日、Julian Day) を求められますか?
Date::Manip も Date::DateCalc もユリウス日を扱いません。 代わりに、ユリウス日の計算を行う例が http://www.perl.com/CPAN/modules/by-module/Time/ にある Time::JulianDay (Timeモジュールにバンドルされています) で見つけられます。
昨日の日付を得るには?
time()関数はある時点からの経過時間を秒で返します。一日分を取りのぞ くには
$yesterday = time() - ( 24 * 60 * 60 );
のようにします。そして、これをC<localtime()>に渡してやれば年、 月、日、時間、分、秒を得ることが可能です。
Perlには2000年問題があるのですか?
短い答: いいえ。Perlには2000年問題はありません。 ただし、あなたの雇っているプログラマーがそうでないように 使っているなら2000年問題はあります。
長い答: この質問は物事の理解を誤っています。 あなたの使っているシャーペンと同じ様にPerlには2000年問題は ありません。perlに組み込みの日付・時刻関数(gmtimeとlocaltime)は 2000年を越えた年も区別するために必要な情報を提供しています (32ビットマシンをトラブルが直撃するのは2038年です)。 これらの関数が配列コンテキストで使われたときに返す年数は 実際の年から1900を引いた値です。1910年から1999年は このやり方ではたまたま二桁の数値となります。 2000年問題を避けるには、年を二桁で扱わないようにします。
gmtime()やlocaltime()は、スカラーコンテキストで呼び出された場合
には完全な年を含んでいるタイムスタンプ文字列を返します。たとえば、
C<$timestamp = gmtime(1005613200)は
$timestamp に ``Tue Nov 13 01:00:00 2001''
をセットします。ここには2000年問題はありません。
このことは、Perlで2000年問題を起こすようなプログラムを作るのに 使えないということではありません。あなたの使うシャーペンも そうであるように。つまり、言語にまつわるミスではなく、使う人の 間違いであるということです。 NRAを刺激するかもしれませんが、 “Perlは2000年問題を打ち破らない。人が打ち破るのだ” ということです。 詳しい説明はhttp://language.perl.com/news/y2k.htmlを参照してください。
入力を検査するには?
この問題に対する回答は、通常は補助的なロジックを伴った正規表現で しょう。詳しくはより限定した質問(数値、電子メイルアドレス、など など)をあたってください。
文字列のアンエスケープ (unescape)をするには?
それはあなたのいう“エスケープ”がなんであるかによります。URLの エスケープはperlfaq9で扱っています。バックスラッシュによるシ ェルエスケープは以下のようにして取り除きます:
s/\\(.)/$1/g;
これは\nだとか\t、あるいはその他の特殊なエスケープを展開しません。
キャラクタの連続した組を取り除くには?
"abbcccd" を "abccd"に変換するには:
s/(.)\1/$1/g;
文字列中にある関数呼び出しを展開するには?
これは perlrefで説明されています。一般的には、これはクォーテ ィングと読みやすさの問題に絡むことですが、可能ではあります。文字 列へ(リストコンテキストで)サブルーチン呼び出しを展開するには:
print "My sub returned @{[mysub(1,2,3)]} that time.\n";
スカラーコンテキストの方がよいのなら、同様なごまかしがやっぱり便 利です。
print "That yields ${\($n + 5)} widgets\n";
Perl 5.004には{...}の中の式にリストコンテキストを与えて
しまうというバグがありますが、これは5.005では修整されていいます。
FAQのこのセクションにある “How can I expand variables in text strings?” も参照してください。
何かがマッチしている/ネストしているということを検出するには?
これは一つの正規表現で解決できないほどの複雑な問題なのです。単一
のキャラクター二つに囲まれた何かを見つけだすには、 /x([^x]*)x/
といったパターンを使えば$1に検査の結果が得られるでしょう。複数キ
ャラクターに囲まれたものの場合は、/alpha(.*?)omega/のようなパ ターンが必要となるでしょう。しかし、ネストしたパターンを扱うよう
なものはありませんし、できません。これに対処するにはパーザーを書
く必要があります。
One simple destructive, inside-out approach that you might try is to pull out the smallest nesting parts one at a time: =cut
もしまじめにパーザーを作ろうと考えているのなら、 それを手助けしてくれるようなモジュールやその他のプログラムが あります。CPANにはParse::RecDescentがありますし、 標準モジュールにはText::Balanceddがあります。byaccプログラム もありますし、Parse::Yaccや Mark-Jason Domunusの優れたpyツールが http://www.plover.com/~mjd/perl/py/ で入手できます。
単純で破壊的なinside-outアプローチもあります。 これは以下のようにして一度に最小のネスト部分を取り出そうという ものです。
while (s//BEGIN((?:(?!BEGIN)(?!END).)*)END/gs) {
# $1に対する操作を行う
}
+A more complicated and sneaky approach is to make Perl's regular +expression engine do it for you. This is courtesy Dean Inada, and +rather has the nature of an Obfuscated Perl Contest entry, but it +really does work: =cut
より複雑で巧妙なやり方にPerlの正規表現エンジンを使うというものが あります。これはDean InadaによるものでObfuscated Perl コンテスト にエントリされるような代物ですが、正しく働きます:
# $_ には解析対象の文字列があります
# BEGINとENDはネストしたテキストの開始と終了とを行います。
@( = ('(','');
@) = (')','');
($re=$_)=~s/((BEGIN)|(END)|.)/$)[!$3]\Q$1\E$([!$2]/gs;
@$ = (eval{/$re/},$@!~/unmatched/);
print join("\n",@$[0..$#$]) if( $$[-1] );
文字列をひっくり返すには?
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は行をジャスティファイしません(左寄せします)。
文字列の最初のN文字にアクセスしたり、それを変更するには?
たくさんのやり方があります。コピーを取りたいのなら、substr()を使い ます:
$first_byte = substr($a, 0, 1);
文字列の一部を変更したいというのであれば、lvalueとしてsubstr()を 使うのが、よく使われている最も単純な方法です。
substr($a, 0, 3) = "Tom";
しかしこういった操作は、パターンマッチングを使った処理が好ましいでしょう。
$a =~ s/^.../Tom/;
何かのN番目のものを変更するには?
自分でN番目の記録を取る必要があります。例えば、(大小文字の違いを無視して)五番
目に現れた"whoever" か "whomever"をC<``whosoever''> に
"whomsoever"変更したいと考えているとしましょう。
$count = 0;
s{((whom?)ever)}{
++$count == 5 # 五番目か?
? "${2}soever" # そうなら交換
: $1 # 元に戻してなにもしない
}igex;
もっと一般的なケースでは、whileループの中で
/g修飾子を使ってマッチの数を数えることもできます。
$WANT = 3;
$count = 0;
while (/(\w+)\s+fish\b/gi) {
if (++$count == $WANT) {
print "The third fish is a $1 one.\n";
# Warning: don't `last' out of this loop
}
}
これは "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 charcters 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";
一行にあるすべての単語をキャピタライズするには?
各単語の最初の文字を大文字にするにはこうします:
$line =~ s/\b(\w)/\U$1/g;
これには、``don't do it''を ``Don'T Do It''にしてしまうような
妙な効果があります。あなたがこういったことをしたいときには、これ
の代わりに以下のようにします(Brian Foyの提案によります):
$string =~ s/ (
(^\w) #行の先頭である
| # もしくは
(\s\w) #空白が先行している
)
/\U$1/xg;
$string =~ /([\w']+)/\u\L$1/g;
行全体を大文字にするにはこうします:
$line = uc($line);
全ての語を小文字にし、それぞれの語の最初の文字を大文字にするには こうやります:
$line =~ s/(\w+)/\u\L$1/g;
プログラムの中にuse localeを置くことによって、 これらのキャラクターがロカールを意識するようにできます
(また、そうすべきです)。 ロカールに関する詳細はperllocaleを参照してください。
これは“title case”として扱われることがありますが、それは 正確なものではありません。例えば映画のタイトルである Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb を考えてみましょう。
訳注:邦題はなんでしたっけ?
どうやれば、 「キャラクター」の内側にある時を除き、「キャラクター」で 終端されている文字列を分割することができるでしょうか?
カンマで分割された文字列を別々のフィールドに置くような例を考えて
みましょう(私たちはここで、カンマで分割された(commna-sparated)で
あり、カンマで終端された(comma-delimited)ではないとしています)。 ここでsplit(/,/)を使うことはできません。なぜなら、クォートの内
側にあるカンマで分割すべきではないからです。例えば以下のようなデ
ータを考えてみましょう。
SAR001,"","Cimetrix, Inc","Bob Smith","CAM",N,8,1,0,7,"Error, Core Dumped"
クォートの制約のためにこれは実に複雑な問題です。ありがたいことに、 私たちには正規表現に関するオススメ本の著者でもあり、この問題を私 たちのために扱ってくれるJeffrey Friedlがいます。彼の提案はこうで す(文字列が$textにあると仮定しています):
@new = ();
push(@new, $+) while $text =~ m{
"([^\"\\]*(?:\\.[^\"\\]*)*)",? # グループはおそらくクォートの内側にある
| ([^,]+),?
| ,
}gx;
push(@new, undef) if substr($text,-1,1) eq ',';
クォーテーションマークで終端されたフィールドの中でクォーテーショ
ンマークを表現したいのならば、それをバックスラッシュで("like \"this\""
のように)エスケープしてください。アンエスケープはこのセクション
の最初のほうにあります。
あるいは、Text::PaserWordsモジュール(標準Perl配布の一部です)を使 ってこうします:
use Text::ParseWords;
@new = quotewords(",", 0, $text);
CPANにはText::CSVモジュールもあります。
文字列の先頭や末尾にある空白を剥ぎ取るには?
最も単純なやり方は多分こういったものでしょう:
$string =~ s/^\s*(.*?)\s*$/$1/;
これは不必要に遅く、破壊的で、かつ文字列に埋め込まれた改行には 対処できません。以下のように二つのステップに分けた方がより早く できます:
$string =~ s/^\s+//;
$string =~ s/\s+$//;
あるいは以下のようにもっとカッコよく書きます:
for ($string) {
s/^\s+//;
s/\s+$//;
}
このイディオムはforeachループでのエイリアシングの 動作を利用したものです。この操作は、複数の文字列に対して
一度に行うことができますし、配列やハッシュの値に対してさえ
使うことができます。
# スカラー、配列、ハッシュの全ての要素の空白を調整します
foreach ($scalar, @array, @hash{keys %hash}) {
s/^\s+//;
s/\s+$//;
}
文字列に空白を詰めたり、数値にゼロを詰めたりするには?
(この回答はUri Guttmanによります)
以下に挙げる例で、$pad_lenは詰め込みたい文字列の長さです。$textや
$numは文字列に詰め込みの対象となる内容を保持していて、$pad_charが 詰め込みに使いたいキャラクターを保持しています。
やっていることがわかっているのなら、$pad_charという変数の代わりに一 文字のキャラクター文字列を使うこともできます。
最も単純なやり方はsprintf関数を使うというものです。
この関数は文字列の左や右に詰め込みを行ったり、0を左に置いたりする
ことができます。
# 空白を伴った左詰め
$padded = sprintf( "%${pad_len}s", $text ) ;
# 空白を伴った右詰め
$padded = sprintf( "%${pad_len}s", $text ) ;
# 0による左詰め
$padded = sprintf( "%0${pad_len}d", $num ) ;
空白やゼロ以外のキャラクターで詰め込みを行いたいのであれば、 以下に挙げるやり方を使うことができます。
これらのやり方はx演算子を使って文字列を生成し、それを
オリジナルのテキストに連結するというものです。
任意のキャラクターによる左詰めと右詰め:
$padded = $pad_char x ( $pad_len - length( $text ) ) . $text ;
$padded = $text . $pad_char x ( $pad_len - length( $text ) ) ;
$textを直接使ったやり方もあります:
$text .= $pad_char x ( $pad_len - length( $text ) ) ;
substr( $text, 0, 0 ) = $pad_char x ( $pad_len - length( $text ) ) ;
文字列から選択されたカラムを取り出すには?
もしあなたが幅ではなくカラムということで考えているのなら、 以下のようなやり方ができます:
#Linuxのpsの出力をカラムで分割するのに必要となるunpackのフォーマットを決める my $fmt = cut2fmt(8, 14, 20, 26, 30, 34, 41, 47, 59, 63, 67, 72);
sub cut2fmt {
my(@positions) = @_;
my $template = '';
my $lastpos = 1;
for my $place (@positions) {
$template .= "A" . ($place - $lastpos) . " ";
$lastpos = $place;
}
$template .= "A*";
return $template;
}
文字列の soundex値を見つけるには?
perlと一緒に配布されている Text::Soundex モジュールを使います。
テキスト文字列の中にある変数を展開するのはどうやればできますか?
以下のような文字列があるとしましょう:
$text = 'this has a $foo in it and a $bar';
変数の両方ともが大域変数であれば、以下のようにしてできます:
$text =~ s/\$(\w+)/${$1}/g; # /e は不要です
もし変数がレキシカル変数であれば、あるいはその可能性があるのなら 以下のようにする必要があるいます:
$text =~ s/(\$\w+)/$1/eeg;
die if $@; # /eではなく/eeが必要
一般的には、対象となる変数を特別なハッシュのエントリに してしまうのが良いかもしれません。例を挙げましょう:
%user_defs = (
foo => 23,
bar => 19,
);
$text =~ s/\$(\w+)/$user_defs{$1}/g;
FAQのこのセクションにある “How do I expand function calls in a string?” も参照してください。
常にクォーティング ``$vars''することの何が悪いの?
そういったダブルクォートが、強制的に文字列化(stringification)す るのが問題で、たとえそれを望んでいなくても数値やリファレンスが強 制的に文字列に変換されてしまうのです。このように考えましょう: ダブルクォートは新しい文字列を生成するのに使われる。 もしあなたがすでに文字列を持っているのであれば、使う必要が あるでしょうか?
以下の例のような変な書き方をすると:
print "$var"; # だめ
$new = "$old"; # だめ
somefunc("$var"); # だめ
あなたはトラブルに巻き込まれることになるでしょう。これらは(99.8%は)、 より単純、かつより直接的に書くべきなのです。
print $var;
$new = $old;
somefunc($var);
さもなければ、プログラムを遅くなることのほかにも、スカラーが実際 には文字列でも数値でもなくリファレンスであるようなときにあなたの プログラムがおかしくなることになります。
func(\@array);
sub func {
my $aref = shift;
my $oref = "$aref"; # 間違い
}
マジカル ++ オートインクリメント演算子やsyscall()関数のような、
文字列と数値の間の違いを実際に気にするようなPerlの幾つかの操作に
おいて、微妙な問題に直面するかもしれません。
文字列化(stringfication)も配列を壊します
@lines = `command`;
print "@lines"; # 間違い - 余計な空白がつく
print @lines; # 正しい
なぜ私の<<HEREドキュメントがうまく動かないのでしょう?
以下の三つの点を確認してください。
ver 4
ヒアドキュメントのテキストでインデントを使いたいのであれば、 以下のようにしてできます:
# 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 white space 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
+=head2 What is the difference between a list and an array?
リストと配列の差とはなんですか?
配列は長さを変えることができます。リストはできません。ある配列に対して
pushやpopができますが、リストに対しては値のセットしかできません。一部には
配列が変数であるのに対してリストは値であると区別して考えている人達もいます。
リストを受け取ったり返したりするサブルーチンはリストコンテキストにあなたを
導き、リストで配列を初期化したり、foreach()でリストを辿ったりできます。
@変数は配列であり、無名配列も配列です。スカラーコンテキストの配列
はその要素数のように振る舞います。サブルーチンはその引数をC<@_>という配列
を通してアクセスし、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`;
を比較したときがそうです。
-wフラグはこのことに関する警告を行います。
配列にあるユニークな要素だけを取り出すのはどうやればできますか?
幾つかの方法が可能です。あなたが決まった順序で取り出したいのか どうか、配列に格納されている順序がどうであるのかによります。
$prev = 'nonesuch';
@out = grep($_ ne $prev && ($prev = $_), @in);
これはとてもよく、余計なメモリも使いません。重複のみを取り除くの にuniq(1)の振る舞いをシミュレートします。これは undef、0、``''のようなfalse値に対してはあまり良くありません。 でも``0 but true''はokです。
undef %saw;
@out = grep(!$saw{$_}++, @in);
@out = grep(!$saw[$_]++, @in);
undef %saw;
@saw{@in} = ();
@out = sort keys %saw; # 必要なければsortを取り除く
undef @ary;
@ary[@in] = @in;
@out = @ary;
しかしおそらくは、あなたはハッシュを使った方が良かったでしょう。
リストや配列の内容にある特定の要素があるかどうかを確かめるには?
ハッシュはこの質問に対する速くて効率の良い解答のために デザインされています。配列はそうではありません。
幾つかのやり方がありますが、この問い合わせを多くのアイテムに対して 行いたいとか、値が任意の文字列である場合には最も速いやり方は元の 配列の逆のものを作って元の配列の値をキーとするような連想配列を保 持するというものです。
@blues = qw/azure cerulean teal turquoise lapis-lazuli/;
undef %is_blue;
for (@blues) { $is_blue{$_} = 1 }
こうすれば、$is_blue{$some_color}がどうであるかでチェックするこ とができます。最初の場所で bulesにハッシュのすべてを保持させるの はよい考えでしょう。
値のすべてが小さな整数であれば、単純な添え字付き配列を使うことが できます。この種の配列はより少ない場所しか使いません。
@primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
undef @is_tiny_prime;
for (@primes) { $is_tiny_prime[$_] = 1; }
これで $is_tiny_prime[$some_number]の内容がどうであるかで チェックすることができます。
問い合わせる値が文字列ではなく整数であるのならば、ビットストリン グを使うことによって大幅に空間を節約することができます。
@articles = ( 1..10, 150..2000, 2017 );
undef $read;
for (@articles) { vec($read,$_,1) = 1 }
これで vec($read,$n,1)が真かどうかで$nの検査ができます。
$is_there = grep $_ eq $whatever, @array;
だとか
$is_there = grep /$whatever/, @array;
のようなやり方はしないでください。
これらのやり方は遅く(最初に対象が見つかったとしてもすべての要素 を検査していしまいます)、非効率(同じ理由です)で、バグの可能性を 含んでいます($whatheverに正規表現キャラクターがあったりしたら?)。 もし一度だけしかテストしていなければ以下のものを使いましょう:
$is_there = 0;
foreach $elt (@array) {
if ($elt eq $elt_to_find) {
$is_there = 1;
last;
}
}
if ($is_there) { ... }
二つの配列の差(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;
}
=head2 How do I test whether two arrays or hashes are equal?
二つの配列や二つのハッシュが等しいかどうかを検査するには?
以下に挙げる例は一レベルの配列に対して有効です。これは文字列としての 比較を使い、definedと未定義の空文字列を区別しません。必要に応じて 修正してください。
$are_equal = compare_arrays(\@frogs, \@toads);
sub compare_arrays {
my ($first, $second) = @_;
local $^W = 0; # 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";
This approach also works for comparing hashes. Here we'll demonstrate two different answers: このアプローチはハッシュの比較にも使えます。以下に二種類の 回答をお見せしましょう:
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";
最初のものは二つのハッシュが同じ内容であると報告しますが、二番目の ものは違うと報告します。
ある条件が真となる最初の配列要素を見つけだすには?
添え字に注意しているのなら以下のようにしてできます:
for ($i= 0; $i < @array; $i++) {
if ($array[$i] eq "Waldo") {
$found_index = $i;
last;
}
}
これで$found_indexにあなたの求めるものが入っています。
リンク付きリストを扱うには?
一般的には、Perlではリンク付きリストを扱う必要はありません。なぜ なら、通常の配列を使ってpushやpop、shiftやunsiftを使って両端で操 作できたり、spliceを使って任意の場所にある任意個の要素を加えたり 削除したりすることができるからです。popとshiftは両方ともが、perl の動的配列に対するO(1)の操作です。shiftやpopがなかった場合、push は一般的にはlog(N)回毎のオーダーで再割り当てが必要になります。 そしてunshiftは呼ばれる毎にポインターのコピーが必要になるでしょう。
もし、本当に、本当にリンク付きリストを使いたいのなら、perldsc や perltootで説明されているようなデータ構造を使うことができ、 アルゴリズムの教科書にあるようなことができます。 例えば以下のようなリストノードをを考えてみましょう:
$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の組み込み型は事実上常に 充分なものなのです。
循環リスト(circular list)を扱うには?
循環リストはリンク付きリストを使って伝統的なやり方で扱うことがで きます。あるいは以下のように配列を使って行うこともできます:
unshift(@array, pop(@array)); # 最後を先頭に
push(@array, shift(@array)); # その反対
配列をランダムにかき混ぜるには?
こうします:
# fisher_yates_shuffle( \@array ) :
# generate a random permutation of @array in place
sub fisher_yates_shuffle {
my $array = shift;
my $i;
for ($i = @$array; --$i; ) {
my $j = int rand ($i+1);
next if $i == $j;
@$array[$i,$j] = @$array[$j,$i];
}
}
fisher_yates_shuffle( \@array ); # @array そのものを入れ替える
spliceを使ったシャッフルアルゴリズムを見たことがあるかもしれません。 カレントの要素をランダムに取り出した別の要素と交換します:
srand;
@new = ();
@old = 1 .. 10; # 単なるデモ
while (@old) {
push(@new, splice(@old, rand @old, 1));
}
これはspliceがO(N)であり、さらにそれをN回呼んでいるのですから 良くありません。つまりこれはO(N**2)のアルゴリズムです。 これは大きな配列に使わなければあなたはその効率の悪さに 気がつかないでしょう。
配列の各要素に対する処理や、変更を行うには?
for/foreachを使います:
for (@lines) {
s/foo/bar/; # 単語を変換
y/XZ/ZX/; # 文字の入れ替え
}
別の方法です。球の体積を求めます:
for (@volumes = @radii) { # @volumes has changed parts
$_ **= 3;
$_ *= (4/3) * 3.14159; # これは定数畳み込みが行われるでしょう
}
同じことをハッシュの値に対して行いたいのであれば、
valuesを使うことはできません。スライスを使う必要があります:
for $orbit ( @orbits{keys %orbits} ) {
($orbit **= 3) *= (4/3) * 3.14159;
}
ある配列からランダムに要素を選択するには?
rand()関数を使います(perlfuncを参照):
# プログラムの先頭で:
srand; # 5.004以降では不要
# その後で
$index = rand @array;
$element = $array[$index];
プログラム毎に一度だけsrandを呼ぶようにしてください。 もし二度以上呼び出すことがあると(先の例のrandの直前で 呼び出すなど)、ほとんどの場合間違ったことを行うことになるでしょう。
N要素を持つリストの順列(permute)を求めるには?
以下の小さなプログラムは入力された行にある各単語の順列をすべて生 成します.関数
permute() で使われているアルゴリズムは任意のリスト
で動作するはずです:
#!/usr/bin/perl -n
# tsc-permute: 入力にある語を入れ替える
permute([split], []);
sub permute {
my @items = @{ $_[0] };
my @perms = @{ $_[1] };
unless (@items) {
print "@perms\n";
} else {
my(@newitems,@newperms,$i);
foreach $i (0 .. $#items) {
@newitems = @items;
@newperms = @perms;
unshift(@newperms, splice(@newitems, $i, 1));
permute([@newitems], [@newperms]);
}
}
}
なにかで配列をソートするには?
sort() (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 ];
これは Schwartzian Transformと呼ばれるトリックを使って以下のよう に書くこともできます:
@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.perl.com/CPAN/doc/FMTEYEWTK/sort.html を参照してください。
後ででてくるハッシュのソートに関する質問も参照してください。
ビット配列を扱うには?
pack() と
unpack()か、vec()とビット演算を使うかします。
以下の例は、$ints[N]がセットされていれば $vecのbit Nをセットします。
$vec = '';
foreach(@ints) { vec($vec,$_,1) = 1 }
そして次に挙げる例は、$vecで与えられるベクターのビットを配列 @ints
に取り出すものです:
sub bitvec_to_list {
my $vec = shift;
my @ints;
# ナルバイトの量を検査してから最善のアルゴリズムを選択します
if ($vec =~ tr/\0// / length $vec > 0.95) {
use integer;
my $i;
# この方法はほとんどがナルバイトのときに高速です
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 {
# この方法は一般的に高速なものです
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によるものです)。
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以降のperlfuncを参照してください。
ハッシュ全体を処理するには?
ハッシュがソートされているかどうかを気にしないのであれば、
each()関数(perlfunc参照)を使います:
while ( ($key,$value) = each %hash) {
print "$key = $value\n";
}
ソートされていることを望むのなら、前の質問にあったようにキーをソ ートした結果に対してforeach()を使う必要があるでしょう。
ハッシュに対して反復操作(iterrating)を行っているときにキーの追加 や削除をすると何が起きますか?
そんなことをしてはいけません。
ハッシュの要素をその値で検索するには?
リバースハッシュを作成します:
%by_value = reverse %by_key;
$key = $by_value{$value};
これは特に効率がよいものではありません。空間を効率よく使うにはこ うします:
while (($key, $value) = each %by_key) {
$by_value{$value} = $key;
}
ハッシュに同じ値がある場合には、このメソッドは最初に見つかったキ ーだけを見つけだします。これはあなたにとっての心配事になるかもし れません。
ハッシュにどれくらいの要素があるのかはどうすればわかりますか?
どのくらいのキーがあるのかという事なら、keys()関数をスカラーコン テキストで使います:
$num_keys = scalar keys %hash;
これをvoidコンテキストで行うと、イテレーターをリセットします。こ れはtieされたハッシュに対して高速なやり方です。
ハッシュを(キーではなく値で)ソートするには?
内部的には、ハッシュはキーと値のペアを特定の順番で取り出すことを 妨げるような方法で格納されています。このため、キーか値のリストを ソートする必要があります.
@keys = sort keys %hash; # キーによるソート
@keys = sort {
$hash{$a} cmp $hash{$b}
} keys %hash; # 値によるソート
以下の例は、値を数値の降順でソートし、二つのキーが同値であればそ れをキーの長さでソートし、それが失敗したならキーの直接的なASCII 比較を行うものです(そう、あなたの使うロカールで代る可能性があり ます。perllocaleを参照してください)。
@keys = sort {
$hash{$b} <=> $hash{$a}
||
length($b) <=> length($a)
||
$a cmp $b
} keys %hash;
私のハッシュを常にソートされた状態にしておくには?
DB_Fileにあるように、DB_Fileモジュールと tie()を使った、$DB_BTREE
ハッシュ束縛を使うことができます。 +The Tie::IxHash module from CPAN might
also be instructive.
ハッシュに対する ``delete'' と ``undef''との間の違いはなんですか?
ハッシュはスカラーのペアです: 最初のスカラーがキーで、二番目のス
カラーが値です。キーは文字列、数値、リファレンスのいずれの種類の
スカラーであっても強制的に文字列にされます。配列の中に$keyと いうキーが既にあれば、exists($key)は真を返します。与えられた キーに対する値はundefとすることができます。これは $array{$key}
を undefにして、$exists{$key}が真を返すという状態です。こ れは ($key, undef)がハッシュに存在しているということを示し ています。
図が助けになるでしょう。以下は%aryのテーブルです:
キー 値
+------+------+
| a | 3 |
| x | 7 |
| d | 0 |
| e | 2 |
+------+------+
そしてこれらが保持している状態はこうです
$ary{'a'} is true
$ary{'d'} is false
defined $ary{'d'} is true
defined $ary{'a'} is true
exists $ary{'a'} is true (perl5 のみ)
grep ($_ eq 'a', keys %ary) is true
ここで undef $ary{'a'}
とすると、テーブルはこうなります:
キー 値
+------+------+
| a | undef|
| x | 7 |
| d | 0 |
| e | 2 |
+------+------+
そしてその状態は以下のようになります。大文字になっているのが 変った場所です。
$ary{'a'} is FALSE
$ary{'d'} is false
defined $ary{'d'} is true
defined $ary{'a'} is FALSE
exists $ary{'a'} is true (perl5 のみ)
grep ($_ eq 'a', keys %ary) is true
最後の二つに注目してください:あなたはundef値を保持していますが、 キーはdefineされているのです!
さて、こんどは以下の例を考えてみましょう:
delete $ary{'a'}
テーブルはこうなります:
キー 値
+------+------+
| x | 7 |
| d | 0 |
| e | 2 |
+------+------+
and these conditions now hold; changes in caps: そしてその状態はこうです。大文字の部分が変ったところです。
$ary{'a'} is false
$ary{'d'} is false
defined $ary{'d'} is true
defined $ary{'a'} is false
exists $ary{'a'} is FALSE (perl5 only)
grep ($_ eq 'a', keys %ary) is FALSE
See, the whole entry is gone! ほら、エントリが丸ごとなくなっていまいました!
なぜわたしのtieされたハッシュは definedとexistsを区別しないのでしょうか?
EXISTS()メソッド と
DEFINED()メソッドとが違うものとして実装され
ていないのかもしれません。たとえば、DBM*ファイルにtieされたハッ
シュにはundefという考え方はありません。これはつまり、上にあった
ture/falseのテーブルがそういったハッシュを使った場合は違ったもに
なるだろうということです。これはまたDBM* fileにとってはexitsと
defineとは同じことであり、そういったものに対して行っていることは
通常のハッシュに対して行っていることとは違うのだということなので す。
each() 操作の途中でリセットしてしまうには?
スカラーコンテキストでkeys %hashを使うと、ハッシュにあるキー の数を返し、そしてそのハッシュに結び付けられたイテレーター
(iterator)をリセットします。ループの途中でlastを使って脱出し ていいて、後でそのループに再度入るようなときには、ハッシュイテ
レーターをリセットしておくためにこれを行う必要があるでしょう。
どうすれば二つのハッシュからユニークなキーを取りだせますか?
まず最初にハッシュからキーを取りだして、それを配列に格納します。 そして、先に説明した配列の一意化 (uniquifying)問題の解決を行いま す。例を挙げましょう:
%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(%myhash, Tie::IxHash);
for ($i=0; $i<20; $i++) {
$myhash{$i} = 2*$i;
}
@keys = keys %myhash;
# @keys = (0,1,2,3,...)
なぜあるハッシュの未定義要素をサブルーチンに渡すとそれを作成するのでしょうか?
If you say something like:
somefunc($hash{"nonesuch key here"});
このようにした場合、この要素は新たに生みだされます(``autovivifies'')。
これはつまり、あなたがそこに何かを格納するため(実際に格納するこ
とがなくても)に作り出されるのです.これは関数が渡されたスカラー
をリファレンスで受け取るからです。somefunc()が$_[0]を変更する のなら、呼び出し元にそれを反映させるために書き込みができるように
なっていなければなりません。
これはperl5.004で修正されました。
通常は、存在していないキーに対するアクセスは、そのキーを生成する ようなことはありません。これはawkの振る舞いとは異なります.
どうすればCの構造体/C++のクラス のハッシュ、配列のハッシュ、配列 と等価なものをPerlで作成できますか
通常はハッシュのリファレンスを使います。多分以下のようになるでしょう:
$record = {
NAME => "Jason",
EMPNO => 132,
TITLE => "deputy peon",
AGE => 23,
SALARY => 37_000,
PALS => [ "Norbert", "Rhys", "Phineas"],
};
リファレンスはperlrefとperlreftutに説明があります。 複雑なデータ構造の例がperldescとperllolにあります。 構造体とオブジェクト指向クラスの例がperltootにあります。
どうすればハッシュのキーとしてリファレンスを使えますか?
これは直接に行うことはできませんが、perlと一緒に配布されている標 準のTie::Rehashモジュールを使うことができます。
バイナリデータを正しく扱うには?
Perlはバイナリクリーンです。ですから、問題はどこにもないはずです。 たとえば、次の例は正しく動作します(ファイルが見つかることを仮定 しています):
if (`cat /vmunix` =~ /gzip/) {
print "Your kernel is GNU-zip enabled!\n";
}
しかしながら一部のシステムでは、“テキスト”ファイルと“バイナリ” ファイルとの間の飽き飽きするようなゲームをする必要があるでしょう。 perlfuncとperlopentutマニュアルページを参照してください。
もし8ビットASCIIデータについて考えているのであれば、perllocale を参照してください。
ただしマルチバイトキャラクターを扱いたいと考えているなら、幾つか の罠(gotchas)があります。正規表現のセクションを参照してください。
あるスカラーが数値/whole/整数/浮動小数点数のいずれであることを 決定するには?
“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" }
if (/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
{ print "a C float" }
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 }
あるいは、 http://www.perl.com/CPAN/modules/by-module/String/String-Scanf-1.1.tar.gz
にあるString::Scanf
をチェックしてみてください。POSIXモジュール(標準perl配布キットの
一部です)は文字列から倍精度浮動小数点数や長整数への変換を適切に 行うstrtolやC<strtod>といったものを提供しています。
プログラムの呼び出しの間に、データ構造を永続的に保持するには?
一部の特定のアプリケーションでは、DBMモジュールの一つを使うこと
ができます。AnyDBM_Fileを参照してください。より一般的には、 CPANにあるFreezeThaw, Storable,
Class::Eroot といったモジュール
をあたってみるべきでしょう。以下にStorableの
storeとretrieveを使った例を挙げます:
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モジュールは その引数を再帰的にコピーする
dcloneという関数を提供しています。
use Storable qw(dclone);
$r2 = dclone($r1);
ここで$r1にはあなたの望むデータ構造のリファレンスを置くことができます。
これは深くコピー(deeply copied)されます。dcloneはリファレンスを取り
リファレンスを返すので、コピーしたいものが配列のハッシュであったりした
場合には余計なpunctuationが必要となるでしょう。
%newhash = %{ dclone(\%oldhash) };
すべてのクラス/オブジェクトのためのメソッドを定義するには?
Use the UNIVERSAL class (see UNIVERSAL). UNIVERSAL クラス (UNIVERSAL)を参照)を使います。
クレジットカードのチェックサムを検査するには?
CPANから Business::CreditCard モジュールを入手してください。
XSプログラムのために倍精度実数や単精度実数の配列をパックするには?
CPANにあるPGPLOTモジュールにあるkgbpack.cというものがそれをします。 倍精度実数や単精度実数を大量に扱うのであれば、CPANにある PDLモジュールを使うことを考えてみるとよいでしょう。これは number-crunchingを簡単にしてくれます。
Copyright (c) 1997-1999 Tom Christiansen and Nathan Torkington. All rights reserved. When included as part of the Standard Version of Perl, or as part of its complete documentation whether printed or otherwise, this work may be distributed only under the terms of Perl's Artistic License. Any distribution of this file or derivatives thereof outside of that package require that special arrangements be made with copyright holder.
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.