0

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

为什么此代码需要第二个“保持”才能正常工作? 没有它,我会收到此错误:

NoMethodError:未定义的方法“包括?” 对于零: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
4

6 回答 6

5

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

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

你自己看:

arr = []
arr << 1              # [1]
arr << 2 unless false # [1, 2]
arr << 3 unless true  # nil
于 2013-06-19T14:18:18.457 回答
2

因为inject/reduce获取块的返回值并用它替换备忘录/累加器。

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

self.each_with_object([]) do |num, keep|
  keep << num unless keep.include?(num)
end
于 2013-06-19T14:14:48.293 回答
1

第二个是在第一次通过之后keep重新初始化keep这里。do |keep,num|见文档enum#inject在第二段最后一行说在迭代结束时, memo 的最终值是该方法的返回值。

于 2013-06-19T14:14:45.950 回答
1

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

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

于 2013-06-19T14:14:49.717 回答
0

第二个keep是必需的,因为您在上一行中的条件。如果keep.include?(num)评估为假,nil将返回到inject累加器。这并不是您真正想要发生的事情:您基本上希望跳过该迭代,但保留前一个数组。第二个keep允许您将数组传递回累加器。

于 2013-06-19T14:17:26.170 回答
0

inject在每个阶段反馈块的输出,并在最后返回块的输出。

当您使用inject聚合数据时,您需要在块的末尾返回接收组合数据的对象,否则在下一次迭代中,块变量将最终指向其他东西,导致奇怪的效果 - 通常是错误。

不过,您不必inject纯粹用于聚合,并且理论上可以在适合您的目的的情况下切换对象。在实践中,我认为这是一种罕见的使用inject

于 2013-06-19T14:20:08.707 回答