2012-02-29 (Wed) [長年日記]
_ returnすべきか、せざるべきか
Rubyについてよくある議論で、returnを書くべきか、省略すべき(途中でreturnするケースは除く)か、というのがある。 いつだったかのRubyConfでコードの書き方についてのパネルみたいなのがあって、会場からTwitterで質問できたので聞いてみたら、会場のほとんどの人がreturnを書かない派で、returnを書く派のパネリストがJava guy!と罵られていた。もっと英語ができたら援護してあげられたのだけど。
僕がreturnを書くのは、
def foo bar baz end
みたいなコードがあった時に、bazが値を返すことを期待しているのか、たんに副作用のために呼んでいるのかを区別しにくいからだ。 単純なgetterなんかではreturnを省略しても問題ないと思うけど。
return書かない派の話を聞くと、例えば関数型言語ではreturnがない(Haskellにはreturnがあるけど全然別物)じゃないか、といった意見が出ることがあるが、関数は値を返すに決まっているからそもそも上記のような問題はない。でもRubyは命令型言語なのだ。
Rubyでも関数型っぽいコードでreturnを省略するのは違和感がない。例えばこんなコード。
def sum_of_squares(ary) ary.map { |i| i ** 2 }.inject(&:+) end
でも次のコードはちょっと不恰好に見える。
def sum_of_squares(ary) result = 0 for i in ary result += i ** 2 end result end
injectを使う時も似たようなことがあって、上のsum_of_squaresのinjectはすっきりしているけど、
def to_hash(ary) ary.inject({}) do |h, (k, v)| h[k] = v h end end
みたいなコードはむしろ格好わるい。
副作用を使うなら割り切って
def to_hash(ary) ary.each_with_object({}) do |(k, v), h| h[k] = v end end
とした方が潔い。
結局、結論としては「読みやすければいいんじゃないの?」というところで、何か面白くなくなってしまったな。
個人的には、Rubyでは値を持たない式がないのですべての(public)メソッドはその返り値に気を配るべきで、内部でしか使わないインスタンス変数の値を露出するなどがないよう、値を返したくないときは最後の行にnilか空のreturnを置くのがいいと思っています。<br>そう決めてしまえば、あとは途中で抜ける以外はreturnは省くというポリシーが取れてシンプルですね。<br><br>そうなっていない所では「これの返り値をふつう知ってるもんかな?」で決めます。(裏返せば、「このコードを見た人が『このコードを書いた人はこれの返り値を意識していないかも』と思わないかな?」)<br><br>・副作用を使うことがもっぱらなメソッド<br>・eachみたいにクラスごとに仕様が違ってnilだったりselfだったり配列だったりするから一見しただけじゃわからんわいというメソッド<br><br>などで終わるときは、その値を返す意思を示したいのであればreturnを付けた方がいいかな、という感じ。<br><br>もちろん、<br><br>・そもそも返り値の仕様がrdocに書いていないメソッド<br><br>のたまたま返す値を使うのは(返り値に限らず)ダメです。
「でも次のコードはちょっと不恰好に見える。」の例は、最後が<br> return result<br>だったとしたらどうか、というのがちょっと気になります。<br># for自体が不恰好とかいう話は置いておいて。
knuさん:<br>値を返さない人だけreturnを書くというのは斬新ですけど、普通の言語と逆ですよねえ。<br><br>なかむら(う)さん:<br>まあ、returnを付けても格好よくはないと思いますけどね。<br>感覚的にはreturnがないと丘サーファーっぽいというか。
や、nilの方が推奨ですよw<br>空のreturnはあんまり好みじゃないです。<br><br>たまたま今perlの仕事をしててそういうポリシーを採っているので、rubyとperl両方に通じる感じで書いてしまいました。(perlではreturn;とreturn undef;は意味が違う)
あ、言いたかったのは(perlではreturn;とundef;は意味が違う)でした。<br>perlでリストを返す関数の場合undef;で終わるのはまずくてreturn;あるいは();が適切、よって何も返したくないときにperlとruby共通で使えるのは空のreturnだなと。
なるほど、Perlも難しいですね。