如何使用下注入红宝石

时间:2014-11-09 18:48:15

标签: ruby-on-rails ruby

我正在编写如下代码:

[1,2,3,4,5].inject([]) do |res, a|
  res << a*a and next if a == 2
  res << a
end

它出现以下错误:

NoMethodError: undefined method `<<' for nil:NilClass

接下来它将res变量设为nil,如何解决此问题?

我尝试了各种各样的方法但是无法接下来使用ruby,我知道我提供的这个片段可以在没有下一个(a == 4 ? res << a*a : res << a)的情况下完成,但在我的实际用例中我有一些复杂的逻辑并且不能简单地完成。

2 个答案:

答案 0 :(得分:11)

替换

res << a*a and next if a == 2

next res << a*a if a == 2

现在,它会起作用。

示例: -

#!/usr/bin/env ruby

ar = [1,2,3,4,5].inject([]) do |res, a|
  next res << a*a if a == 2
  res << a
end

p ar
# >> [1, 4, 3, 4, 5]

Read the documentation of next

  

next可以取一个值,该值将是块当前迭代返回的值....

答案 1 :(得分:2)

唉。不要使用尾随条件。相反,使用标准if / else

可轻松完成
[1,2,3,4,5].inject([]) do |res, a|
  if a == 2
    res << a*a
  else
    res << a
  end
end
# => [1, 4, 3, 4, 5]

每当你觉得自己想把自己写进一个角落时,不要寻找出路,而是要备份并查看你想要完成的事情,看看是否有在那里是一种更直接的方式。

为了便于阅读,我可能会稍微调整一下代码。长期维护依赖于快速了解正在发生的事情,而复杂或不明显的代码可能会在以后造成损失:

[1,2,3,4,5].inject([]) do |res, a|
  if a == 2
    res << a*a
  else
    res << a
  end

  res # return it for clarity in what the block is returning
end
# => [1, 4, 3, 4, 5]

injecteach_with_object类似,只是它依赖于块末尾返回的累加器,这就是为什么我要在块的末尾添加res的原因为清楚起见,由于if块。切换到each_with_object会消除对块的返回值的依赖,从而允许以下代码在逻辑上更清晰:

[1,2,3,4,5].each_with_object([]) do |a, ary|
  if a == 2
    ary << a*a
  else
    ary << a
  end
end
# => [1, 4, 3, 4, 5]

当然,在那一点上,整个事情可以进一步减少,并可以利用三元版本:

[1,2,3,4,5].each_with_object([]) do |a, ary|
  a2 = (a == 2) ? a * a : a
  ary << a2
end
# => [1, 4, 3, 4, 5]

以上两个中哪一个更具可读性取决于编码它的人和负责维护它的人。我倾向于非三元版本,因为它更容易扩展/扩展,并且没有三元?:链的线噪声。


由于评论中提到了map减少了一些噪音,我们应该如何转换数组:

[1,2,3,4,5].map { |a|
  (a == 2) ? a * a : a
}

未经测试,但看起来是正确的。