我正在编写如下代码:
[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
)的情况下完成,但在我的实际用例中我有一些复杂的逻辑并且不能简单地完成。
答案 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]
inject
与each_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
}
未经测试,但看起来是正确的。