注射结果为零

时间:2012-02-11 10:39:04

标签: ruby

如果符合某个条件,我会尝试累积一些值。 为什么这个片段返回nil,当我希望它返回2?

[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0) { |s, e| s + e[1] if e[0] <= 1}

inject不是正确的方法吗?

3 个答案:

答案 0 :(得分:6)

你应该返回s;

[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0) { |s, e| s += e[1] if e[0] <= 1; s}

小清洁

[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0){|s,(k,v)| s += (k<2 ? v : 0)}

答案 1 :(得分:3)

你可以分多步完成:

>> a = [[1, 2], [2, 3], [3, 8], [4, 2]]
>> a.select { |e| e.first <= 1 }.inject(0) { |s, e| s += e.last }
=> 2
>> a.select { |e| e.first <= 1 }.map(&:last).inject(0, :+)
=> 2

使用单个inject完成所有操作应该更有效率,但将其分解为碎片可能更清晰,除非您拥有非常大的阵列,否则速度差异不会明显。

如果您不介意使用哈希或数组模拟指针,可以使用each_with_object执行此操作:

>> a.each_with_object({ :sum => 0 }) { |(k,v), m| m[:sum] += v if k <= 1 }[:sum]
=> 2
>> a.each_with_object([0]) { |(k,v), m| m[0] += v if k <= 1 }.first
=> 2

inject块的结果将在下次调用您的块时用作s的值。在第二个迭代器上,这个:

s + e[1] if e[0] <= 1

的值为nil,因为e[0]将为2.后续迭代也会从您的块中返回nil,因为除了第一个e[0]之外,每个s都大于1 。这就是您需要从块中返回[[1, 2], [2, 3], [3, 8], [4, 2], [1, 11]] 的原因。如果你有这样的数组:

nil

那么你甚至不会从inject中获得NoMethodError: undefined method `+' for nil:NilClass ,你只能例外:

nil

当您的区块尝试将11添加到{{1}}时。

答案 2 :(得分:2)

另一种方式:

xs.map { |k, v| v if k <= 1 }.compact.inject(0, :+)

请注意Ruby如何从缺乏列表推导中受到影响,我们必须(有些低效率)使用map + compact来模拟它。在使用LC的语言中,它看起来更好:sum(v for (k, v) in xs if k <= 1)