関数の出口の数について

404 Blog Not Found:perl - even more best practices

色々やり取りがあってなかなか興味深いなーとか。

でこの中で「無精で短気で傲慢なプログラマ」さんが関数の戻り値は基本はひとつが良いとか書かれてたんだけどそれってどうなの?

確かに専門学校時代には先生からそんな風に教えてもらった記憶があるけど、
無理に出口をひとつにするのは可読性を悪くするだけだと思うんだけどなぁ・・・。
例を挙げると

■インデントが深くなりやすい

弾さんも言ってましたけどこれはかなりのデメリットです。
コードが縦にも横にも長くなりやすいです。

■戻り値用の一時変数を用意

出口をひとつにするってことは戻り値用の変数を用意してそいつをずっとみてまわらないといけなくなりますよね。

 =head2 func()
 
  適当な関数の仕様
 
 引数が渡されなかったら戻り値なし。
 引数が未定義だったら 0 を返す。
 引数が数値じゃなかったら 0 を返す。
 引数が10000以上だったら 0 を返す。
 引数が0だったら 1 を返す。
 引数が偶数だったら 2 を返す。
 引数が奇数だったら 3 を返す。
 
 =cut
 
 sub func {
     my ($param) = @_;
     my $ret; # 戻り値用の変数
 
     if( @_ ) {
         if( defined $param ) {
             if( $param =~ /^\d+$/ ){
                 if( $param < 10000 ) {
                     if( $param == 0 ) {
                         $ret = 1;
                     }else{
                         if( $param % 2 == 0 ){
                             $ret = 2;
                         }elsif{
                             $ret = 3;
                         }
                     }
                 }else{
                     $ret = 0;
                 }
             }else{
                 $ret = 0;
             }
         }else{
             $ret = 0;
         }
     }
 
     return $ret;
 }

例えば上記のような感じで$retとか用意して条件によって値を入れていくわけですね。

このコードを調査することになったとします。
出口をひとつにはしてるけど結局$retに何が入っているかを調べていくわけですね。

とすると結局returnを複数書こうが$retを使おうが見る箇所は変わらないわけです。
むしろ一時変数を用意してしまうとreturn $retする前のどこかで$retを書き換えてないかとかを注意深く見る必要があります。

また、処理が必要無くなったら即returnするようにしとけばreturn以降の処理を気にしなくていいのがメリットですね。
即returnしないってことはelseを常に意識しないといけないのがしんどいわけで。
処理が長い場合やifがかなりネストしてる場合は特にしんどいです。
例えば$paramが未定義だったらもうこの関数でやることはないのでその時点でreturnしておいたらそこまでを見てればいいのに対し、
returnしなければそれ以降の処理(つまりelseの処理)で$paramが使われてるかもしれないとか常にコードに目を光らせないといけなくなります。
上記例のコードが短いのであまりそんな感じはしませんけどね。

で、下記が僕的コードです。

 sub func {
     my ($param) = @_;
 
     return if !@_;
     return 0 if !defined $param;
     return 0 if $param !~ /^\d+$/;
     return 0 if $param >= 10000;
     return 1 if $param == 0;
     return 2 if $param % 2 == 0;
     return 3;
 }

極端な関数の例でしたが、どっちが見やすいでしょうか。
縦にも横にも短い、一時変数を用意しない、処理する必要がなくなったら即returnしてるのでコードを見る範囲が狭まる。

まぁ確かに見やすさは人それぞれな部分もあるかもしれませんけど少なくとも無理してまで出口の一本化をするメリットが僕には感じられないってのが結論ですね。
というか実際問題出口を一つにすることのメリットってあるの?

なんかそーゆーのを紹介してる記事とか無いかな。読んでみたい。