Ruby .inject() - 需要帮助理解这段代码

时间:2013-06-19 14:12:06

标签: ruby

此代码采用数组并仅返回唯一值。

为什么此代码需要第二个“保持”? 没有它,我得到这个错误:

  

NoMethodError:未定义的方法`include?'为零:NilClass

class Array
  def my_uniq_inject
    self.inject([]) do |keep, num|
      keep << num unless keep.include?(num)
      keep  # why is this required?
    end
  end
end

6 个答案:

答案 0 :(得分:5)

您可能会感到困惑,因为通常arr << x会返回arr,所以您会认为自己很好。

unless部分可以搞砸了。如果数组中的最后一个元素不是唯一的(即它已经出现在数组的前面),那么unless子句将使表达式计算为nil

亲眼看看:

arr = []
arr << 1              # [1]
arr << 2 unless false # [1, 2]
arr << 3 unless true  # nil

答案 1 :(得分:2)

因为inject / reduce获取了块的返回值并用它替换了memo / accumulator。

您可以使用不替换备忘录的each_with_object

self.each_with_object([]) do |num, keep|
  keep << num unless keep.include?(num)
end

答案 2 :(得分:1)

第二个keep用于在第一次传递后重新初始化keep此处do |keep,num|。请参阅文档enum#inject在第二行最后一行说明在迭代结束时,备注的最终值是方法的返回值。

答案 3 :(得分:1)

在块的末尾需要keep,因为块的结果用作下一次inject迭代的累加器。

如果没有keep,则块中的第一行有时会返回keep,但有时会返回nil(特别是当条件不满足时)。

答案 4 :(得分:0)

由于前一行中的条件,第二个keep是必需的。如果keep.include?(num)的计算结果为false,则nil将返回inject累加器。那不是你想要发生的事情:你基本上希望跳过这个迭代,但保留前面的数组。第二个keep允许您将数组传递回累加器。

答案 5 :(得分:0)

inject在每个阶段输回块的输出,并在结束时返回块的输出。

当您使用inject来聚合数据时,您需要返回在块结束时接收组合数据的对象,否则在下一次迭代中,块变量将最终指向其他内容,造成奇怪的影响 - 通常是一个错误。

您不必将inject纯粹用于聚合,并且理论上可以根据您的目的切换对象。在实践中,我认为这是inject

的罕见用法