caseで変数のクラスを比較しようとすると上手く行かない


ある変数の型をチェックして、そのクラスによって処理を変えて、他のところに渡すような処理を考えていました。その処理自体は一行程度で済むので、どうせなら`case`で書き換えてもいいだろうと思って、書き換えてみたところ、上手く動いてくれません。

具体的には、以下のようなコードです:

[code]

     def type_check_with_if n
       if n.class == String
         "文字列だ！"
       elsif n.class == Hash
         "ハッシュだ！"
       end
     end
    
     def type_check_with_case n
       case n.class
       when String  then "文字列だ！"
       when Hash    then "ハッシュだ！"
       end
     end
    
     test_seed = ["foobar", {:foo => :bar}]
     p test_seed.map {|n| type_check_with_if n }
     # => ["文字列だ！", "ハッシュだ！"]
     p test_seed.map {|n| type_check_with_case n}
     # => [nil, nil]
    
[/code]

本当なら、`type_check_with_case`でも`["文字列だ！",
"ハッシュだ！"]`という出力を期待しているのですが、結果は上記の通り、`nil`の配列です。

もしかして、自分は`case`の仕様を何か勘違いしているのでしょうか? もし、勘違いしていたとするならば、参考情報を含めて教えて頂ければ嬉しいです。




まずは正しいコードを紹介します。  
`n.class` の `.class` を消すと期待した通りに動きます。

[code]

    def type_check_with_case n
       case n
       when String  then "文字列だ！"
       when Hash    then "ハッシュだ！"
       end
     end
    
[/code]

以下はその理屈です。

case文そのものの仕組みについてはこちらのサイトで詳しく説明されているのでここでは割愛します。

<http://d.hatena.ne.jp/shibason/20090617/1245231492>

上のサイトでも説明されているとおり、case文では `===` メソッドが使われます。

つまり

[code]

    String === "str"
    => true
    
[/code]

ですが

[code]

    String === "str".class 
    => false
    
[/code]

です。

また、

[code]

    String.class
    => Class
    
[/code]

です。

`Class` クラスの `===` メソッドはどこで定義されているのかというと、親クラスの `Module` クラスで定義されています。

<http://docs.ruby-lang.org/ja/2.2.0/class/Class.html>

<http://docs.ruby-lang.org/ja/2.2.0/class/Module.html#I_--3D--3D--3D>

> self === obj -> bool  
>  指定された obj が自身かそのサブクラスのインスタンスであるとき真を返します。 また、obj
> が自身をインクルードしたクラスかそのサブクラスのインスタンスである場合にも 真を返します。上記のいずれでもない場合に false を返します。
>
> 言い替えると obj.kind_of?(self) が true の場合、 true を返します。

結構ややこしい話になっていますが、case文では `==` ではなく `===` で比較されているということを理解しておいてください。

[code]

    String == "str".class
    => true
    
[/code]

ですが

[code]

    String === "str".class 
    => false
    
[/code]

になるんです。

うまく説明できているかどうかわかりませんが、参考になれば幸いです。

P.S.

僕も今回初めて理屈をじっくり調べてみました。

[code]

       case n
       when String  then "文字列だ！"
       when Hash    then "ハッシュだ！"
       end
    
[/code]

みたいな書き方はイディオムとして当たり前のように使っているので、わざわざ理屈は考えていませんでした。  
僕と同じく大半の人は「それはそういうふうに動くようになっているから」という理解で止まっているんじゃないかな～と思ったりします。

P.S. その2

こういったRubyの言語仕様を疑問に思った場合は、以下の書籍を読んでみると詳しく説明されていたりします。

[プログラミング言語
Ruby](http://www.amazon.co.jp/gp/product/4873113946?ie=UTF8&camp=1207&creative=8411&creativeASIN=4873113946&linkCode=shr&tag=junic05-22&qid=1421319342&sr=8-1&keywords=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E+ruby)

書籍内のバージョンは1.9止まりなので最新のRubyの言語仕様は一部カバーできていませんが、それでも大半の内容は今でも有効なので、リファレンスとして一冊持っておくことをオススメします。




まずは正しいコードを紹介します。  
`n.class` の `.class` を消すと期待した通りに動きます。

[code]

    def type_check_with_case n
       case n
       when String  then "文字列だ！"
       when Hash    then "ハッシュだ！"
       end
     end
    
[/code]

以下はその理屈です。

case文そのものの仕組みについてはこちらのサイトで詳しく説明されているのでここでは割愛します。

<http://d.hatena.ne.jp/shibason/20090617/1245231492>

上のサイトでも説明されているとおり、case文では `===` メソッドが使われます。

つまり

[code]

    String === "str"
    => true
    
[/code]

ですが

[code]

    String === "str".class 
    => false
    
[/code]

です。

また、

[code]

    String.class
    => Class
    
[/code]

です。

`Class` クラスの `===` メソッドはどこで定義されているのかというと、親クラスの `Module` クラスで定義されています。

<http://docs.ruby-lang.org/ja/2.2.0/class/Class.html>

<http://docs.ruby-lang.org/ja/2.2.0/class/Module.html#I_--3D--3D--3D>

> self === obj -> bool  
>  指定された obj が自身かそのサブクラスのインスタンスであるとき真を返します。 また、obj
> が自身をインクルードしたクラスかそのサブクラスのインスタンスである場合にも 真を返します。上記のいずれでもない場合に false を返します。
>
> 言い替えると obj.kind_of?(self) が true の場合、 true を返します。

結構ややこしい話になっていますが、case文では `==` ではなく `===` で比較されているということを理解しておいてください。

[code]

    String == "str".class
    => true
    
[/code]

ですが

[code]

    String === "str".class 
    => false
    
[/code]

になるんです。

うまく説明できているかどうかわかりませんが、参考になれば幸いです。

P.S.

僕も今回初めて理屈をじっくり調べてみました。

[code]

       case n
       when String  then "文字列だ！"
       when Hash    then "ハッシュだ！"
       end
    
[/code]

みたいな書き方はイディオムとして当たり前のように使っているので、わざわざ理屈は考えていませんでした。  
僕と同じく大半の人は「それはそういうふうに動くようになっているから」という理解で止まっているんじゃないかな～と思ったりします。

P.S. その2

こういったRubyの言語仕様を疑問に思った場合は、以下の書籍を読んでみると詳しく説明されていたりします。

[プログラミング言語
Ruby](http://www.amazon.co.jp/gp/product/4873113946?ie=UTF8&camp=1207&creative=8411&creativeASIN=4873113946&linkCode=shr&tag=junic05-22&qid=1421319342&sr=8-1&keywords=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E+ruby)

書籍内のバージョンは1.9止まりなので最新のRubyの言語仕様は一部カバーできていませんが、それでも大半の内容は今でも有効なので、リファレンスとして一冊持っておくことをオススメします。

