読者です 読者をやめる 読者になる 読者になる

初代Masteries

きっとモヒカンにもなれないお前たちに告げる!!!

演算子の優先順位で後輩が困惑していた話

研究室の後輩, @が, 「このスクリプト, なんかうまく動かないんすけど原因わかりますか?」と聞いてきました.

use strict;
use warnings;

chomp(my $a1 = shift);
chomp(my $a2 = shift);

print "$a1 + $a2: " . $a1 + $a2 . "\n";
print "$a1 - $a2: " . $a1 - $a2 . "\n";
print "$a1 * $a2: " . $a1 * $a2 . "\n";
print "$a1 / $a2: " . $a1 / $a2 . "\n";

ぱっと見, 問題なさげなスクリプトです.
一方, 実行結果はこんな感じ.

$ perl calc.pl 1 2
Argument "1 + 2: 1" isn't numeric in addition (+) at calc.pl line 7.
3
Argument "1 - 2: 1" isn't numeric in subtraction (-) at calc.pl line 8.
-1
1 * 2: 2
1 / 2: 0.5

「*と/はうまくいって, +と-だけうまくいかないんすよねぇ...」との事.

結果から言えば, こうなってしまうのは, Perlの演算子の優先順位が 「*, / > +, -, .」になっているのが理由です.
ちなみに, Perlの演算子の優先順位は, perldoc.jpのperlopに書かれています.

perlopを見てみると, 演算子'+'と演算子'.'は同じ優先順位になっています.
加えて, これらの演算子は左結合なので, 左から順番に処理されていきます. すなわち,

print ((("$a1 + $a2: " . $a1) + $a2) . "\n");

という順番で処理されるので, 演算子'+'で文字列を足そうとしてしまい, 怒られます.

一方, 演算子'*'と演算子'/'は, 演算子'.'よりも優先順位が高い為, 先に計算が行われ,

print ((("$a1 * $a2: " . ($a1 * $a2)) . "\n");

...という順番で処理されるので, 問題なく実行できたという訳です.

ちなみに, yanchaで@さんに指摘して頂いたのですが, Perlが内部でどのような順番で演算を行なっているかは, Deparseを使うことで簡単に確認できます.

$ perl -MO=Deparse,-p calc.pl
use warnings;
use strict;
chomp((my $a1 = shift(@ARGV)));
chomp((my $a2 = shift(@ARGV)));
print(((("$a1 + ${a2}: " . $a1) + $a2) . "\n"));
print(((("$a1 - ${a2}: " . $a1) - $a2) . "\n"));
print((("$a1 * ${a2}: " . ($a1 * $a2)) . "\n"));
print((("$a1 / ${a2}: " . ($a1 / $a2)) . "\n"));
calc.pl syntax OK

困ったらDeparse, 覚えておいて損はないですね.