From: blake.h.l.west@... Date: 2019-01-30T19:13:12+00:00 Subject: [ruby-dev:50777] [Ruby trunk Feature#14973] Proposal of percent literal to expand Hash Issue #14973 has been updated by blakewest (Blake West). I just want to +1 this. I think it's a great idea, and had raised it independently in [15236](https://bugs.ruby-lang.org/issues/15236). It doesn't seem like Matz has given an opinion on it though either here or in 15236. Can we bump this to see what his thoughts are? What's the best way to do that? Thanks - Blake ---------------------------------------- Feature #14973: Proposal of percent literal to expand Hash https://bugs.ruby-lang.org/issues/14973#change-76596 * Author: osyo (manga osyo) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- ## 概要 変数名から `{ 変数名: 変数の値 }` という Hash を定義するための %記法の提案です。 以前からちょくちょく[提案されていた](https://bugs.ruby-lang.org/issues/14579) ```ruby x = 1 y = 2 h = {x:, y:} p h #=> {:x=>1, :y=>2} ``` のような ES6 ライクな構文を `{}` 構文ではなくて %記法で定義するものになります。 ## 仕様 ```ruby hoge = 1 foo = 2 bar = 3 # スペース区切りの変数名をキー、変数の値を Hash の値として展開する %h(hoge foo bar) # => { hoge: 1, foo: 2, bar: 3 } ``` これは以下と同等の処理になります。 ```ruby hoge = 1 foo = 2 bar = 3 { hoge: eval("hoge"), foo: eval("foo"), bar: eval("bar") } # => { hoge: 1, foo: 2, bar: 3 } ``` ### ローカル変数以外 内部で `eval` を使用しているので、そのコンテキストで評価できればローカル変数以外も使用することが出来ます。 ```ruby def meth "meth" end @hoge = 42 Foo = "str" p %h(meth @hoge Foo $stdout) # => {:meth=>"meth", :@hoge=>42, :Foo=>"str", :$stdout=>#>} ``` キーは変数名そのままです(`$` や `@` がついたまま。 ### 重複したキーがある場合 キーが重複している場合、`{}` と同様に2回目以降は無視されます。 ```ruby hoge = 42 foo = "str" %h(hoge foo hoge) # => {:hoge=>42, :foo=>"str"} ``` ### キーの変数が存在しない場合 ```ruby hoge = 42 foo = "str" %h(hoge foo bar) # Error: undefined local variable or method `bar' for main:Object (NameError) ``` ### Hash 内で展開 Hash なので `**` で展開できます。 ```ruby hoge = 42 foo = "str" { bar: "bar", **%h(hoge foo) } # => {:bar=>"bar", :hoge=>42, :foo=>"str"} ``` ## 式展開 `%I` などと同様に式展開を行います。 ```ruby hoge = 42 foo = "hoge" %h(#{foo}) # => {:hoge=>42} ``` ## ユースケース ### キーワード引数に渡す ```ruby Model = Struct.new(:tag, :name, :age, keyword_init: true) def create_human(name:, age:) # ** で Hash を展開して渡す Model.new(tag: :human, **%h(name age)) # Model.new(tag: :human, name: name, age: age) end name = "mami" age = 15 # 変数をまとめて渡す create_human(%h(name age)) # => # # create_human(name: name, age: age) ``` ### デバッグ出力として利用する ```ruby class Hash # 適当なデバッグ出力用メソッド def debug_output each { |key, value| puts "#{key}: #{value}" } end end def create(name, age) # 引数を確認するためのデバッグ出力 %h(name age).debug_output end name = "homu" age = 14 create(name, age) # output: # name: homu # age: 14 ``` ## その他 * キーは Symbol で定義 → String 版もあったほうがよいだろうか * 式展開のみ実装 → `%i` みたいな式展開を行わないでほしいユースケースが思いつかなかった * なぜ `%h` という名前? → `Hash` だから… 以上、任意の変数名から Hash を展開する `%h` 記法の提案でした。 ご意見等あればコメント頂けると助かります。 ## 関連するチケット https://bugs.ruby-lang.org/issues/11105 https://bugs.ruby-lang.org/issues/14579 https://bugs.ruby-lang.org/issues/13137 ---Files-------------------------------- hash_expand.patch (7.3 KB) -- https://bugs.ruby-lang.org/