Ruby: case 式における何もしない else の扱い

先週末に WEB+DB PRESS Vol.99の「良いコード」を本気でコードレビューしてみた という興味深いブログ記事が書かれました。

はてなブックマークでのコメントを見ると、多くの人がブログ記事の内容に賛同しているようですが、「P12: 何もしないelseは必要か?」の節には異論が散見されました。


ブログ記事全体の趣旨は、WEB+DB PRESS Vol.99の「Rubyで学ぶ!良いコードって何だろう?」という特集記事への批判です。

特集記事では、次のような例が掲載されていて、

case hour
when 9
  say_hello()
when 18
  say_goodbye()
end

これは意図が伝わりにくいから、次のように空の else 節を加えるべしと書かれています。

case hour
when 9
  say_hello()
when 18
  say_goodbye()
else
  # 何もしなくてよい
end

これに対して、ブログ記事の筆者(伊藤氏)は空の else 節は不要だと主張しています。


プログラミング初心者が else 節を書き忘れる可能性を考えると、このケースでは空の else 節を書いたほうが親切かなと私は思います。

しかし、まったく空の else 節が残ることに「座りの悪さ」を感じるのも確かです。

はてなブックマークのコメントで知ったのですが、Python には「何もしない」 pass 文というのがあるそうですね。

これが使えればいいのですが、残念ながら Ruby には存在しません。


特集記事があげている例があまりよくなかったのかもしれません。関数 say_hellosay_goodbye の中身まで含めたリファクタリングを検討すべき場面じゃないでしょうか。

おそらく、ソースコード全体はこんな感じです。

def say_hello
  puts "Hello!"
end

def say_goodbye
  puts "Good bye!"
end

hour = Time.now.hour

case hour
when 9
  say_hello()
when 18
  say_goodbye()
end

であるとすれば、まずこう書き換えるべきです。

def say(message)
  puts "#{message}!"
end

hour = Time.now.hour

case hour
when 9
  say("Hello")
when 18
  say("Good bye")
end

すると、次のように自然な形で else 節を導入できます。

def say(message)
  puts "#{message}!"
end

hour = Time.now.hour

message = case hour
  when 9
    "Hello"
  when 18
    "Good bye"
  else
    nil
  end

say(message) if message

元のコードの問題点は、(1)メッセージを選択する論理と(2)メッセージを出力するかどうかを決定する論理を単独の case 式に混在させてしまった点にあります。

私のコードでは、二つの論理が明確に分離されています。(1)の論理は case 式で表現され、(2)の論理は「後置の if」で表現されています。

いかがでしょうかね。