利用者:Senseequal/Perl
chomp(my $answer = <STDIN>)
[編集]これはまずいです。Ctrl-D (WindowsではCtrl-Z) などで入力が中断された場合、$answerにはundefが代入され、そのままchomp()に渡されてしまいます。
warningsが有効な場合、
Use of uninitialized value $answer in chomp at -e line 1.
と警告されます。
undefで例外を出す場合は、
my $answer = <STDIN>;
defined $answer or die "Aborted.";
chomp $answer;
undefも有効な値として扱う場合は、
my $answer = <STDIN>;
chomp $answer if defined $answer;
undefを空文字列として扱う場合は、
my $answer = <STDIN>;
if (defined $answer) {
chomp $answer;
}
else {
$answer = '';
}
もしくは
my $answer = <STDIN> || '';
chomp $answer;
のようにしましょう。
myとlocal
[編集]myとlocalは全く別物なので、この章のように同じ場所で紹介するのは好ましくありません。
myは局所変数を宣言するもの、localは大域変数を局所化するものなのですが、このような説明で理解できるのは上級者だけだと考えられます。Goto文は有害だと考えられる。
myのみを先に紹介し、localは後から必要になったら紹介する、というのが良いのではないでしょうか。
myは変数を使う前に必ず前置するものです。
また、
for my $node ($doc->getElementsByTagName('p')) {
# ...
}
for (my $i = 0; $i < $#_; $i += 2) {
# ...
}
while (my $line = <$fh>) {
# ...
}
このように、ループとmyは密接な関係にあります。
localというのは、$/や$\などの特殊変数を一時的に別な値にしたい場合に使います。
my $data = do { local $/; <$fh> };
while (1) {
local $\ = "\n";
print join '', qw( y e s );
}
$_
[編集]$_は、「それ」に相当するものです。
# 行を取り出して
while (<$fh>) {
print $_; # それを出力
}
しかし、日常生活動作において「行を取り出してそれを出力してください」などと言うことはまずありません。
普通は「行を取り出して出力せよ」でしょう。「それ」はつけなくても良いのです。
print while <$fh>;
()をつけるかつけないか
[編集]組み込み関数の()は省略することができます。
print join '', map { $_ } map $_, grep { $_ } grep /$_/, grep $_, "Hello, world!\n";
では、次の場合はどうでしょうか。
if (length $str + 1 > 50) {
# ...
}
lengthは名前付き単項演算子 (nup) ですが、perldoc perlopによれば、nupの優先順位は+よりも低く、>よりも高いといいます。
つまり、これは
if (length($str + 1) > 50) {
# ...
}
と解釈されてしまいます。
これを防ぐためには、どうすればよいかというのがこの章の議題です。
最も簡単なのは、組み込み関数の()をつけることです。
if (length($str) + 1 > 50) {
# ...
}
しかし、次のような方法もあります。
if ((length $str) + 1 > 50) {
# ...
}
式としての()を付けるというものです。
普通、後者の方法は見にくいので、頑なに組み込み関数の()を省略するのではなく、優先順位の関係でつけなければならない場合は妥協する、前者の方が良いとされています。
しかも、前者のほうが1バイト短くて済みます。
ただし、
print (2+3)*4;
このような状況では、
print((2+3)*4);
よりも
print+(2+3)*4;
print +(2+3)*4;
のほうが見やすいと考えられます。
ハッシュと山カッコ演算子
[編集]while (<$hash{abc}>) {
# ...
}
は失敗します。
<>はreadline()を用いて実装されているので、
while (readline $hash{abc}) {
# ...
}
とすればよいでしょう。
<>と同じように、
while (defined($_ = readline $hash{abc})) {
# ...
}
と解釈されます。
定数
[編集]自由を基調とするPerlにおいて、定数はほとんど必要ないものと考えられます。
our $PI = 4 * atan2(1, 1);
とでもして、あとは改変しないという暗黙のルールを定めておけばよいでしょう。
あるいはInternals::SvREADONLY()を使います。[1]
ユニークなユニーク
[編集]warn keys%{{map{$_=>1}@ARGV}};
演算子とスペース
[編集]中置演算子は両側に半角空白を入れるのが見やすいとされていますが、->だけは特例として半角空白を入れません。
他の言語において、->や.(メソッド呼び出し演算子)を演算子に含めるかどうかは必ずしも明確ではないからです。
これにはオブジェクト指向というものが絡んでいます。
また、単項演算子にスペースをいれるのは変だと考えられます。
if (! $socket) {
die "Can't connect to $host";
}
プロフェッショナルとアマチュアの違い
[編集]アマチュア:
if ($x < 0) {
print "$x は負の数\n";
}
elsif ($x > 0) {
print "$x は正の数\n";
}
else {
print "$x は 0\n";
}
プロ:
printf "%sは%s\n", $x,
$x < 0 ? '負の数'
: $x > 0 ? '正の数'
: 0;
スカラコンテキストの強制
[編集]一般にはscalar()を使うのが意味的にも妥当だが、ワンライナーなどにおいては
print''.localtime
print$_=localtime
sub _($){shift};print+_+localtime
のような方法も考えられます。
柔軟さの例
[編集]1の後に0が999個付く文字列を考えます。一つの解は
$ perl -Mbignum -le "print 1e999"
です。しかし、初等的な文字列演算の範囲でも解は求められます。文字列連結演算子と文字列反復演算子を用いて
$ perl -le "print 1.0x999"
としますが、これは正しくありません。なぜなら
$ perl -le "print 1.0x999"
=
$ perl -le "print 1.0 x 999"
=
$ perl -le "print 1 x 999"
このように文字列連結演算子が小数点として解釈されるためです。これを回避し、
$ perl -le "print 1 . 0x999"
としますが、これも正しくありません。なぜなら、
$ perl -le "print 1. 0x999"
=
$ perl -le "print 1 . (0x999)"
このように0xから始まる数値が十六進法として解釈されるためです。これを回避し、
$ perl -le "print 1 . 0 x 999"
が正解です。
コンテキスト
[編集]Perlのコンテキストを理解するには、「コンテキスト」という言葉を一旦忘れてしまった方がよいです。
Perlが文脈を考慮しているとか、空気を読んでいるなどと感じられるのは、Perlの臨機応変な文法をほとんど知り尽くした上での結論であって、Perlを習い始めた人に対して「Perlには文脈がある」と言っても理解しがたいのではないかと思います。
まずは、「スカラに配列を代入したらどうなるか」という素朴な疑問から始めます。
my @array = qw( A B C D E )
my $scalar = @array;
print $scalar; # 5
これは配列の要素数が返されていることが分かります。
これを難しく言うと「配列をスカラコンテキストで評価すると要素数が返る」となります。スカラコンテキストとは簡単に言えば、「スカラに代入されている文脈」、もう少し正確に言えば、「スカラとして評価できる値が返されることが期待されている文脈」となるでしょうか。
まあ、そんなことは気にせずに実験を進めます。
今度は逆、配列にスカラを代入したらどうなるでしょうか。
my @array = qw( A B C D E );
my $scalar = 42;
@array = $scalar;
print @array; # 42
もとあった配列の要素はすべて消えて、3が配列の最初の要素になっていることがわかります。配列は上書きされてしまうのです。
(中略)
いままでスカラ、リスト、配列、ハッシュの12通りの組み合わせを説明しました。ここで大切なのは「ハッシュをスカラに代入するとどうなるか」を覚えることではなく、代入される相手が何なのかによって、返される値が異なるという現象が存在するということを知っておくことです。
そして、
$scalar = @array;
これは代入相手がスカラだったために、@arrayが要素数へ変換されて
$scalar = 5;
となったと解釈します。
代入が行われる前に、@arrayが5に変換されていると考えるのです。これは次の話を簡単にするためで、言語仕様上の意味はありません。
さて、これは代入という演算に限らず、あらゆる演算に一般化されます。たとえば、print演算子も含まれます。
my @array = qw( A B C D E );
print @array;
print @arrayというのは、print演算子に@arrayを渡しています。ここで、もしprint演算子がスカラの性質をもっているならば、@arrayは5(要素数)に変換され、5と表示されるはずです。上記のコードの実行結果は次のようになります。
ABCDE
5ではありませんでした。printはスカラではなかったようです。先の実験では、配列をxに代入して要素が表示されるのは、x = リストの場合だけでした。そう、printはリストの性質なのです。
このようにして説明していけば、コンテキストもいくらか分かりやすいのではないでしょうか。
すべての演算子でリストコンテキストが採用されていること、スカラは要素が1のリストと考えることができるため、リストコンテキストはスカラコンテキストよりも優先されることを説明し、最後にスカラコンテキストを強制するscalar演算子でも紹介しておけばいいでしょう。
基数変換
[編集][2]を参考にして作成。
汎用モジュールとしてはMath::BaseCalcなどがありますが、基本的なものは組み込み関数で変換可能。
from\to | 文字列 | 二進法 | 八進法 | 十進法 | 十六進法 |
---|---|---|---|---|---|
文字列 | 'A' | unpack 'B8', 'A' | sprintf '%o', ord 'A' | ord 'A' | unpack 'H2', 'A' |
二進法 | pack 'B8', '01000001' | 0b1000001 | sprintf '%o', unpack 'C', pack 'B8', '01000001' | unpack 'C', pack 'B8', '01000001' | unpack 'H2', pack 'B8', '01000001' |
八進法 | chr oct 101 | unpack 'B8', pack 'C', oct 101 | 0101 | oct 101 | sprintf '%x', oct 101 |
十進法 | chr 65 | unpack 'B8', pack 'C', 65 | sprintf '%o', 65 | 65 | sprintf '%x', 65 |
十六進法 | pack 'H2', 41 | unpack 'B8', pack 'H2', 41 | sprintf '%o', hex 41 | hex 41 | 0x41 |
もしよろしければPerl/関数に転記。