人は1行の記述で死ぬこともある
公開からコンスタントに閲覧をいただいている以下の記事。需要がありそうなので、もう1つ記事を書いてみようと思う。
題材は前回同様のJavaScriptのクソコードではあるが、前回は冗長を極めたクソコードであるのに対し、今回はたった1行である。
1行なのに、なんと示唆深い(決してほめていない)コードだろうか。
不可思議なコード
では、実際に見てみよう。
return a = a >= 10 ? 10 : a >= 5 ? 5 : a >= 3 ? 3 : a >= 2 ? 2 : 1, z * a;
これを見て、何が return
で返されるかわかります?
代入演算子
最初見たとき、代入文 a = ×××
の評価値って何になるのかな? a
が return
されるのかなと思いました。
割り当て操作は、割り当てられた値として評価されます。
JavaScriptの仕様を確認し、「return a = ×××
は、a
がローカル変数である限り、 return ×××
ということか、冗長だな」と判断しました。
しかし、このコードは a
を返すことはありません。
閑話休題(?) 三項演算子
この1行の大半を占める三項演算子 ? :
は、もはや筆者には見慣れたものだ。規約で使うなといわれることが多いが、私はそれほど抵抗はない。
しかし、演算子の優先順位が重要なこの例示の1行では、 ?
の前の条件式部分には、せめて ()
で括って意味のまとまりを見せておきたいかな?
(a >= 10) ? 10 : (a >= 5) ? 5 : ………
三項演算子のネストがひどいのではないかという読者も多いだろうが、筆者はこれを見たときにはそんなに感情は動かなくなった。a
を対象として比較する一貫性があるので、まだ見られるコード。
カンマ演算子(コンマ演算子)
この行の最後は、1, z * a
で終わる。私は当初、すべての三項演算子で 偽 と判定されたら、z * a
が返されるのかなと思っていた。カンマ演算子の仕様を見てみよう。
それぞれの演算対象を(左から右に)評価し、最後のオペランドの値を返します。
ただ、筆者の理解は間違っていた。このカンマ演算子は、優先順位が最低位の演算子であることを見落としていた。しかも、前方の 代入演算子 =
よりも後に評価される。
つまり、1, z * a
をカタマリとして理解してはダメで、最後のオペランドとしては z * a
のみであり、return
される対象は、return
から一番遠い、z * a
なのだ。
,
の前の a = a >= 10 ? 10 : a >= 5 ? 5 : a >= 3 ? 3 : a >= 2 ? 2 : 1
は、返す値を決めるために計算しているだけだったのだ。
いみじくもWikipediaを見ていると、以下のようにある。
左被演算子を評価しその値を捨て、その後右被演算子を評価する演算子である。
だいぶ後ろに記述されたカンマ演算子ごときでその値が捨てられるのである。頭から机上デバッグしていたレビューアーは、「捨てんのかい!」とツッコミを入れずにはいられない。
1行にすることの罪
1行で何でもしようとすると、デバッガでブレークポイントを置きづらいのも苛立つポイントである。デバッグ目的で2行以上に変更すればブレークポイントも貼れるのだが、改行によって処理が変わらないか確認するのも難しい。
処理の順序
前述の不可思議なコードを書き直してみよう。
if(a >= 10) {
a = 10;
}
else if(a >= 5) {
a = 5;
}
else if(a >= 3) {
a = 3;
}
else if(a >= 2) {
a = 2;
}
else {
a = 1;
}
return z * a;
上記は、解釈しやすいように記述しているので、行数でいえば冗長かもしれない。ただし、return
で何が返ってくるのはz * a
とわかりやすくなった。
せめて下に示すぐらいには分けられなかったのかね。
const b = ((a >= 10) ? 10 : (a >= 5) ? 5 : (a >= 3) ? 3 : (a >= 2) ? 2 : 1);
return z * b;
そうそう、別に a
に代入する(a
に書き戻す)必要はないよね。
まとめと学び
- 代入文の評価値は、代入した値である
- 三項演算子はネストもできる
- カンマ演算子の評価値は、最も右にあるオペランドである
- 可読性の観点から1行書く限度がある
この1行は、結果的に学びにはなった。だからといって感謝はしていない。
コメント@fujitanozomu(藤田 望)
8 @www-tacos
6 @jaque(編集済み)
2 @so_asa 5 @otn 1 @Nabetani(鍋谷 武典)(編集済み)
0 @albireo
Cで,が使われる典型例
0 @i_tatte(いたて)
4 @belq 0 @makoto-developer(makoto-developer) 0 @D-Three
0 @ashworth(ash worth)
0 @ashworth(ash worth) 0 @ashworth(ash worth) 0 @ashworth(ash worth) 0 @ashworth(ash worth)(編集済み)
0
さっさとreturnしちゃうのが良いんじゃないですかね。
おもしろさの中に学び(カンマ演算子)もあって良い記事でした
個人的には、ネストする三項演算子は改行とインデントが必須だと思っています。
https://qiita.com/yuba/items/9832c26dc1498e0017bb
なので今回のコードもいい感じに整形したら意外とありかも?と思って書いてみましたが、やっぱりダメですね。(再代入とカンマ演算子がksすぎる)
@fujitanozomu さん
おっしゃる通り、私もすぐに返すでいいかなとも思いました。
私なら最後の
return
は、1倍であることを明示するために、以下のように書きたいですかね。書き忘れだと思っちゃいそうだし・・@www-tacos さん
果敢なチャレンジ、ありがとうございます
そもそも
return
の後ろにカンマ演算子を置くのが、アンチパターンですね。個人的には、インデントはVSCodeからフォーマッタに任せたいので三項演算子は1行で書き、記事にある通り、三項演算子のまとまりをカッコで括りたい派です。
それで見づらいようなら
if
文を使ってしまうかも。minifyされたコードではこのカンマの使い方は基本的なテクニックですね。知っているとプロダクションビルドされたコードが読みやすくなります。
returnなのにaに代入してる時点で「後ろのほうで参照するんだな。これは心して読まねば」と察するべきということでしょうね
私としては、左が大きい不等号つらい、不要な再代入は避けたい、一行が長い、括弧があったほうがいい、という印象は持ちましたが、それほどわかりにくいとは感じませんでした。(個人の感想です)
初見で苦なく理解できました。倍率の計算とかですかね。
条件演算子の部分は左から順に読めばわかるので高難易度とは思いませんでした。
難易度が高いと感じるのは下記のようなものです。
それと
a
がローカル変数であればで済むはずなのに、わざわざ
としているので、
return
後もa
の値が活きるのかもしれないと想像していまして、もしそうなら、そのための代入がreturn
内で行われている点がひどいと感じます。蛇足ですが、PHP は三項演算子の優先順位が違う(ことがある?)ので要注意です。
参考になれば幸いです。
このコンマ演算子はC由来のもので、式しか書けない場所に複数の処理を書いたりするのに使われてました
でもCの文法をベースにしているjavaやC#が採用しなかったように、問題点も認識されていました。
javascriptがそんな
,
演算子を採用したのは意図的なものと言ってよさそうなので、考え方の違いを感じられる箇所ですね。@otn さん
「returnの右辺に代入文」の実用的な使われ方として思い浮かぶのは「返値を変数にも保存しておきたい」を一行で済ませるといったケースですね。
たぶんこのコードを書いた人は関数型プログラミング言語を書いてきた人なんでしょうね。
みたいな処理をそのまま直訳して
と書いてしまったような感じがします。関数型として見ればかなり自然な書き方なので。
仮にこれをレビューする立場になったら、
可読性/メンテナンス性が低いので冗長な記述に変えてください
くらいしか浮かばなかったけど(主観だから心のなかで反論されそう)
デバッグしにくいってのもあるのか
これをASTに変換するとどういう式になるんだ。冗長なコードとあまり変わらない?
これは...何をしたいかなんとなくわかるけど、もっとこう...あるだろうってなりますね。
頭がまともならこうじゃねぇの?
あまりにもバカらしいので動くかどうか検証してないけど。
あれかな? 掛け算には順序があります!とか言ってる脳みそウニな小学校算数信奉者なんかな?
なんでreturnするだけでカンマがどうしたいう話になるんやろ?
https://qiita.com/jaque/items/b99ed9dce78cc64fa9d2#comment-df9cc431b87878f1d3dc
これ、酷いなぁ…。
こんなのにいいねが6もつくんや…。
世も末やなぁ…。
分かったようなこといってるけど、
変数定義したら負けなんか?君らの知能だと…。
頭痛くなってくるわ…。
必要もないカンマ演算子とやらを使っただけで
「おー!興味深いですねぇー!!!」
とか盛り上がるから
世間一般からは『プログラマはキモい』って思われてるし、
全く信頼されてないんよなぁ…。
「エンジニア不足です」って言われてる“エンジニア”は、
そいうキモオタの事じゃないねんけどなぁ…。
はぁ!?
何を言ってんねん…。頭痛くなってくるわ…。
上の文章と下の文章で整合性取れてないやろ。
分けるべきが結論なら何に代入しようが関係ないやん。
ほんま、力抜けるわ…。
いいね以上の気持ちはコメントで