おがた (@xtetsuji) です。インフラ志望で10年以上前にオープン系IT業界に入ったのですが、今では当時やろうとも思わなかったウェブプログラマーをして生活しています。
今回はタイトルの通り、二項演算子の間の空白の意味。コードレビューがある文化の人達はこういう議論をしたことがあるのかな。私はそういう文化がほとんどなかったので、単に持論を持っているだけでして、今回はその持論を書いてみようかなと思ってブログを書き始めました。Qiitaに書こうかなと思ったんですが、エッセーに近いのでたまにはプログラム的話題をこっちにも書いてみようかなと。
事の発端
先日、会社でJavaScriptの初心者向け研修が行わていて、自分は参加対象じゃなかったのですが、講師の話が漏れ聞こえてきました。「あぁ、基礎的なことをやっているんだな」って感じでしたが、講師が言った「イコールは代入です。イコールを代入に使ってしまったので数学的な比較はイコールイコールになりました」とか、そうだよねぇって聴いていたわけなんですが、一つ漏れ聞こえてきた解説で気になったことがありました。
「イコールの両端の空白は見やすさのためです」
いや、それは半分正しくても、本当の意図はそこじゃないんじゃないの?と。
演算子の優先順序
演算子には優先順序があります。JavaScriptにも当然ながら演算子の優先順序があります。
私は、空白や空白類文字がこの演算子の優先順序や結合力を惑わしてはいけないし、それを表現するようになっているべきと思っています。
空白が表現する演算子の優先順序
一番簡単な例は、加減乗除でしょう。足し算や引き算よりも掛け算や割り算のほうが優先順位が高くて、左から計算される(評価される)ことは中学までに習うことで、誰もが知っていることです。
2980円のものを買って消費税がかかったんだけど1000円の割引クーポンを持っていたとしましょう。
2980 * 1.08 - 1000
こういう計算式を書くのが普通で、普通に左に掛け算があるから左から計算しても大丈夫だと思うでしょう。ただ、こう書くとどうでしょう。
2980 * 1.08-1000
マイナス記号の両方に空白がありません。普通のプログラム言語では二項演算子の前後の空白についてはあまり気にされませんし、まさに見やすさ以外の意味はないのですが、あたかも「くっついているほう」が先に計算されると、油断している時や心が弱っている時に勘違いしてしまうと思いませんか?
これは誰もが知っている二項演算子の簡単な例ですが、もし普段あまり使わない二項演算子の組み合わせでこれをやられて惑わされたとき、あなたは正しい判断ができるでしょうか。
Perlにも演算子の優先順序がありますが、空白で惑すことはいくらでもできます。
my $result = 2 * 3 ** 5
2に3の5乗をかけているのですが、空白が等しく入っているので、べき乗演算子 “**” の優先順位が掛け算演算子より高いことに気づかない人は惑わされるでしょう。一番良いのはカッコを使って明示的に優先順位を表すことですが、プロジェクト内で比較的自明な場合は空白のありなしで結合力の強弱を表すことが暗に行われることもあると思います。これは簡単な例なので良いのですが、カッコだらけになって見づらくなるという意見もあるので、なかなか悩ましいです(Lispをしこたまやっていると、カッコが見えなくなるというプログラマ的進化を遂げられるらしいのですが)。
my $result = 2 * 3**5;
PerlでもJavaScriptでも、代入演算子 “=” の優先順序は非常に低いのです。なので、より優先順序の高い比較結果の真偽値を入れるということもできます。
var bool = x == y; // x == y の真偽値を bool に入れる
あれ、でもなんだか見づらいですね。カッコを入れるか、”==” の両端の空白を無くすか、色々なやり方があると思います。私も個人で書くプログラム、GitHubやCPANで公開するプログラム、社内プロジェクトで書くプログラムで、書き方をわけるかなーとは思います。
単項演算子や三項演算子
今回の話とはあまり関係ない話ではありますが、三項演算子 ” ? : ” の優先順位は、CやC系の言語やPerlでは「右結合」なのですが、PHPでは「左結合」なの、それを知らないとネストした三項演算子でハマる可能性があるので気をつけたほうがよいです。というかPHPでネストした三項演算子は(たとえカッコを付けて順序を明示しても)書かないほうがいいとすら私は思っています。C系の言語の普通の常識で読めないから。
単項演算子は二項演算子より高い優先順位を持っているケースがほとんどなので、少なくとも私自身は、文法上不必要な空白を入れて離す書き方をするのはケースバイケースかなと思っている派です。Perlでも、組み込み関数に見えるものが実際は単項演算子やそれと同等の優先順位だったりする場合があって、その場合には優先順位が高くなります。たとえば ref とか scalar とか。JavaScriptでもtypeofなどがこれに当たるのかな。これは英単語のキーワードなので、変数と空白一つを挟んで書く必要があったり、そのほうが自然な場合がありますが(Perlだとシジルがあるからくっつけて書けてしまう場合もあるかもしれないけど、なんか気持ち悪い)、あまり詳しくない人とソースコードを共有する場合には、明示的にカッコを付けて書くほうがよいかもしれません。
よくあるのは、否定演算子 “!” が変数とくっついていると見づらいから離す書き方。演算子の性質上、離して書いてもあまり大勢に影響がない場合が多いのですが、結合力を惑わさないように、私は否定演算子 “!” と変数を離して書かない派です。見づらいという意見には確かに同意しています。特にPerlのようにシジルがつく場合はそう。各エディタのシンタックスハイライトでどうにかしてほしいものですね。
まとめ
というわけで “=” の両端に空白を入れることの大きな意味は、代入演算子 “=” が大抵のプログラム言語の二項演算子としての優先順位でも最下位かかなり最下位に近いところにいるから、という、持論のお話でした。
たぶんプログラマの数だけ意見あるところではあると思いますが、あくまで一人のプログラマーの一つの持論ということで軽く考えていただければと思います。もし参考になったら幸いです。