5510


先日activesupportの`Object#instance_values`メソッドのソースコードを読んでいたら  
Hashについてわからないことがあったので質問させていただきます.

[code]

    Hash[] #=> {}
    Hash[[]] #=> {}
    Hash[[[]]] #=> invalid number of arguments
    Hash[[[1,2],[3,4]]] #=> {1=>2, 3=>4}
    
[/code]

上に挙げたこれらのコードの挙動を教えていただけますでしょうか.

下記はactivesupportの`Object#instance_values`のソースコードです.

[code]

    def instance_values
      Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
    end
    
[/code]

<https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/instance_variables.rb>


CC BY-SA 3.0
2015-01-28T06:57:09.120
5501
2015-01-29T04:22:14.253
2015-01-29T04:22:14.253
85
7797
question
ruby
Hash[[[1,2],[3,4]]] のようなHashの挙動はどのように解釈すればいいですか？


面白いなと思ったので調べてみました。間違っている可能性がありますので、その際にはコメントでご指摘をおねがいします。

仕様が参考になると思いますので、`Ruby 2.2.0`時点での`Hash`に関するリファレンスを貼っておきます。  
<http://docs.ruby-lang.org/ja/2.2.0/method/Hash/s/=5b=5d.html>

これによるとHash[]とした時、引数によって内部で行われる処理が変わりそうです。

  * self[other] -> Hash
  * self[*key_and_value] -> Hash

ざっくりとですが [ruby/hash.c at trunk ·
ruby/ruby](https://github.com/ruby/ruby/blob/trunk/hash.c)
も参考にしました。([rb_hash_s_create](https://github.com/ruby/ruby/blob/trunk/hash.c#L566)関数が該当部分になります。)  
質問にあるそれぞれのケースについて見てみます。

### Hash[] #=> {}

`self[*key_and_value] -> Hash`の引数が0個の場合に相当し、空のHashが返ってきます。

### Hash[[]] #=> {}

`self[*key_and_value] -> Hash`で引数が分解されますが、要素数0のため、1つ前のケースと同じ結果となります。

### Hash[[[]]] #=> invalid number of arguments

`self[*key_and_value] -> Hash`で引数が分解されますが、要素数が1(`[]`)で奇数となるため失敗します。

尚、エラー出力箇所は 606行目あたり(変動可能性あり)の

[code]

     rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)",
        RARRAY_LEN(v));
    
[/code]

だと考えます。

### Hash[[[1,2],[3,4]]] #=> {1=>2, 3=>4}

`self[*key_and_value] -> Hash`の説明の通り、KeyとValueの繰り返しとして格納される。

* * *

**追記**  
尚、`Ruby`のバージョンによって挙動が異なる件ですが  
[hash.c: raise on invalid input · 8d6add9 ·
ruby/ruby](https://github.com/ruby/ruby/commit/8d6add973ebcb3b4c1efbfaf07786550a3e219af)あたりが関わっているように見えました。この修正のきっかけとなったバグは1.9.2の頃に報告されていて、2.0.0で修正されたようです。


CC BY-SA 3.0
2015-01-28T07:44:29.423
5509
2015-01-29T03:01:58.290
2015-01-29T03:01:58.290
3313
3313
5501
answer


該当する部分は [Hash.[]](http://www.rubydoc.info/stdlib/core/2.1.0/Hash.%5B%5D)
と思われます。

>
[code]

>     .[](key, value, ...) ⇒ Object
>     .[]([ [key, value)) ⇒ Object
>     .[](object) ⇒ Object
>  
[/code]

>
> Creates a new hash populated with the given objects. Equivalent to the
> literal { key => value, ... }. In the first form, keys and values occur in
> pairs, so there must be an even number of arguments. The second and third
> form take a single argument which is either an array of key-value pairs or
> an object convertible to a hash.
[code]

>     Hash["a", 100, "b", 200]             #=> {"a"=>100, "b"=>200}
>     Hash[ [ ["a", 100], ["b", 200] ] ]   #=> {"a"=>100, "b"=>200}
>     Hash["a" => 100, "b" => 200]         #=> {"a"=>100, "b"=>200}
>  
[/code]

要約するとハッシュのコンストラクタには、以下のようにブロックを渡せますが、

[code]

    Hash{key1 => value1, key2 => value}
    
[/code]

別の呼び出し方として、以下のバリエーションがあると受け取れます。

  * `.[](key1, value1, key2, value2, ...)`
  * `.[]([[key1, value1], [key2, value2], [...))`
  * `.[](object)`

そこで、質問に挙げられたコードを見ていきます。

  * `Hash[] #=> {}`  
これは、要素無指定なので、空のハッシュが生成されました。

  * `Hash[[]] #=> {}`  
これは、`.[]([[key1, value1], [key2, value2], [...))` の指定ですが、 「key, value
ペアがありません」でした。よって空のハッシュです。

  * `Hash[[[]]] #=> invalid number of arguments`  
これは、`.[]([[key1, value1], [key2, value2], [...))` の指定ですが、 「key1= **無指定** ,
value1= **無指定** 」 となり、key, value ペアを指定するための[]があるのにもかかわらず、要素の数が 0 でした。 よって
"invalid number of arguments" エラーとなります。

  * `Hash[[[1,2],[3,4]]] #=> {1=>2,3=>4}`  
これは、`.[]([[key1, value1], [key2, value2], [...))` の指定ですが、 `1=>2`, `3=>4`
が指定され新しいハッシュが生成されました。

※ コメントにありますが、 `Hash[[[]]]` についてはバージョンによって挙動が異なるようです。

**追試:**

上記の説明で、特に、`.[]([[key1, value1], [key2, value2], [...))`
の動作について、`hash.c`を追いながら追試しました。説明で抜けていたのは、 「`key1` の値だけが与えられると、 `value1=nil`
とされる」ことです。以下に例を示します。

[code]

    # tested on ruby 2.0.0p598
    
    Hash[[]]
    # => {}
    
    Hash[[], 1]
    # => {[]=>1}
    
    Hash[[[]]]
    => invalid number of elements (0 for 1..2)
    
    Hash[[[[]]]]
    # => {[]=>nil}
    
    Hash[[[[]]], 1]
    # => {[[[]]]=>1}
    
    Hash[[[[[]]]]]
    # => {[[]]=>nil}
    
    Hash[[[[[]], 1]]]
    # => {[[]]=>1}
    
[/code]


CC BY-SA 3.0
2015-01-28T07:46:42.027
5510
2015-01-28T09:17:07.940
2015-01-28T09:17:07.940
4978
4978
5501
answer