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

当我运行它时,我得到一个索引错误

当我将块更改为

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

然后它工作。

ruby 如何处理第一次没有得到我想要的注入尝试?
有一个更好的方法吗?

4

4 回答 4

13

仅仅因为 Ruby 是动态和隐式类型的,并不意味着您不必考虑类型。

没有显式累加器的类型Enumerable#inject(通常称为reduce)类似于

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

或者我刚刚编造的更红宝石的符号

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

您会注意到所有类型都是相同的。的元素类型,Enumerable块的两个参数类型,块的返回类型和整体方法的返回类型。

Enumerable#inject 带有显式累加器(通常称为)的类型fold类似于

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

或者

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

在这里,您看到累加器可以具有与集合的元素类型不同的类型。

这两条规则通常可以帮助您解决所有Enumerable#inject相关的类型问题:

  1. 累加器的类型和块的返回类型必须相同
  2. 不传递显式累加器时,累加器的类型与元素类型相同

在这种情况下,规则#1 会咬你一口。当你做类似的事情时

acc[key] = value

在您的块中,分配评估为分配的值,而不是分配的接收者。您必须将其替换为

acc.tap { acc[key] = value }

另请参阅为什么 Ruby 注入方法无法总结没有初始值的字符串长度?


顺便说一句:您可以使用解构绑定使您的代码更具可读性:

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,第二个块返回哈希,所以它确实有效。

很多人认为这种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 回答