12
a = [[1, 'a'],[2, 'b'],[3, 'c'], [4, 'd']]
a.inject({}) {|r, val| r[値[0]] = 値[1]}

これを実行すると、インデックスエラーが発生します

ブロックを

a.inject({}) {|r, val| r[val[0]] = val[1]; r}

その後、動作します。

ルビーは、私が望むものを得られない最初の注入試行をどのように処理していますか?
これを行うより良い方法はありますか?

4

4 に答える 4

13

Ruby が動的かつ暗黙的に型付けされているからといって、型について考える必要がないわけではありません。

Enumerable#inject明示的なアキュムレータを持たない の型(これは通常 と呼ばれreduceます) は、次のようなものです。

reduce :: [a] → (a → a → a) → a

または、私が作成したよりRubyっぽい表記で

Enumerable[A]#inject {|A, A| A } → A

すべてのタイプが同じであることがわかります。の要素の型Enumerable、ブロックの 2 つの引数の型、ブロックの戻り値の型、およびメソッド全体の戻り値の型。

明示的なアキュムレータ (これは通常 と呼ばれます) をEnumerable#inject 持つの型は、次のfoldようなものです。

fold :: [b] → a → (a → b → a) → a

また

Enumerable[B]#inject(A) {|A, B| A } → A

ここでは、accumulator がコレクションの要素の型とは異なる型を持つことができることがわかります。

これらの 2 つの規則により、通常、関連するすべてのEnumerable#inject型の問題を解決できます。

  1. アキュムレータの型とブロックの戻り値の型は同じでなければなりません
  2. 明示的なアキュムレータを渡さない場合、アキュムレータの型は要素の型と同じです

この場合、あなたを悩ませているのはルール 1 です。あなたが何かをするとき

acc[key] = value

あなたのブロックでは、割り当ては、割り当ての受信者ではなく、割り当てられた値に評価されます。これを次のように置き換える必要があります

acc.tap { acc[key] = value }

なぜ Ruby の inject メソッドは初期値なしで文字列の長さを合計できないのですか?も参照してください。


ところで: 分解バインドを使用して、コードをより読みやすくすることができます。

a.inject({}) {|r, (key, value)| r[key] = value; r }
于 2012-05-14T00:56:00.407 に答える
9

もっと簡単な方法があります -

a = [[1, 'a'],[2, 'b'],[3, 'c'], [4, 'd']]
b = Hash[a] # {1=>"a", 2=>"b", 3=>"c", 4=>"d"}

最初の方法が機能しない理由は、inject がブロックの結果をr次の反復で使用するためです。最初の反復でrは、 は渡された引数に設定されます。この場合は{}です。

于 2012-05-13T20:46:17.903 に答える
4

最初のブロックは代入の結果を に戻しinject、2 番目のブロックはハッシュを返すため、実際に機能します。

多くの人がinjectアンチパターンのこの使用法を検討しています。each_with_object代わりに検討してください。

于 2012-05-13T20:45:47.663 に答える
3
a.inject({}) { |r, val| r.merge({ val[0] => val[1] }) }
于 2012-07-30T02:58:26.790 に答える