【问题标题】:Reduce returns nil on summation减少总回报为零
【发布时间】:2012-09-21 05:39:03
【问题描述】:

我正在计算一个项目在枚举中出现的次数。

irb(main):003:0> (1..3).reduce(0) {|sum, p| sum += 1 if p == 1}
=> nil
irb(main):004:0> (1..3).find_all{|p| p == 1}.length
=> 1

reduce 方法似乎应该与 find_all 方法具有相同的行为。为什么它返回nil 而不是1

irb(main):023:0> (1..3).reduce(0) {|sum, p| sum += 1 if p == 2}
NoMethodError: undefined method `+' for nil:NilClass
    from (irb):23:in `block in irb_binding'
    from (irb):23:in `each'
    from (irb):23:in `reduce'
    from (irb):23
    from /usr/bin/irb:12:in `<main>'

第一次迭代出现问题。 reduce就不能这样用吗?

【问题讨论】:

  • p 不是1sum += 1 if p == 1 的结果是nil。块的结果分配给sum,因此 nil 将覆盖您的 sum 值。

标签: ruby reduce


【解决方案1】:

在reduce中,代码块中的值被赋值给累加器。在您的情况下,您将第一个分配覆盖为与后来的 nil 相加。

您可以通过以下方式解决此问题:

(1..3).reduce(0) {|sum, p| sum += 1 if p == 1; sum}

(1..3).reduce(0) {|sum, p| sum += p == 1 ? 1 : 0}

对于您的第二个示例, sum 在第一次迭代时被分配为 nil,而您试图在第二次迭代时将 1 添加到 nil。

请记住,reduce/inject 可能不是最好的计数工具 - 试试

(1..3).count(1)

【讨论】:

  • 检查@maerics 的答案以了解编写该块的更好方法
  • 这取决于您想回答什么 - OP 问题(为什么?)或如何更好地实现这一点(就像 ruby​​ 中的所有内容一样,它可能非常主观。有人可能会争辩说,例如 sum + p==1 ? 1 : 0 是更好地捕捉计数语义)。显然,更好的实现方式是根本不使用inject,可以(1..3).count(1)
  • 理想情况下,您可以同时解释这两种情况:“您可以通过这样做来解决它,但最好的方法就是这样做”
【解决方案2】:

Enumerable#reduce method 的块的返回值(或最后一个值)总是在每次调用时存储为累加器的新值,因此就地递增总和(@ 987654323@) 具有误导性。如果p==1,则您的块返回预期值,否则返回nil,因此累加器被覆盖。

尝试修改您的块以始终返回累加器的预期值(总和),例如:

(1..3).reduce(0) { |sum,p| (p==1) ? sum+1 : sum }

reduce 方法实现中的调用顺序如下所示:

acc = 0
acc = yield(acc, 1) # (sum=0, p=1) => sum+1 => 1
acc = yield(acc, 2) # (sum=1, p=2) => sum => 1
acc = yield(acc, 3) # (sum=1, p=3) => sum => 1
acc # => 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 2022-08-04
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    • 2021-04-14
    • 2015-12-16
    相关资源
    最近更新 更多