此代码采用数组并仅返回唯一值。
为什么此代码需要第二个“保持”? 没有它,我得到这个错误:
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
答案 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