5

我正在尝试Ruby中的 Project Euler中的问题 6(在我尝试学习该语言的过程中),这是我在第一次迭代中提出的:

upto = 10
a = (1..upto).to_a.product((1..upto).to_a)
#a.each{ |x| print "(#{x[0]}, #{x[1]})\n"}
puts a.inject(0) {|sum, x| sum + x[0]*x[1] if (x[0] != x[1])}

不幸的是,这在 Ruby 2.0 上引发了以下错误:

block in <main>': undefined method +' 中表示 nil:NilClass (NoMethodError)

更令人费解的是,当我删除 if 条件时没有遇到错误(这显然给了我错误的答案!)

upto = 10
a = (1..upto).to_a.product((1..upto).to_a)
a.each{ |x| print "(#{x[0]}, #{x[1]})\n"}
puts a.inject(0) {|sum, x| sum + x[0]*x[1]} #if (x[0] != x[1])}

上面给出了以下输出(在打印出 a 的元素之后):

3025

作为调试步骤,我什至打印了 'a' 的内容,以确保没有 nil 元素——结果很好。有人可以解释

  1. 我在这里做错了什么?
  2. 为什么当我省略“if”条件时会有所不同,因为错误消息在“+”运算符中,否则会无条件执行?

编辑:获得关于实现相同解决方案的替代、更优雅的方法的评论也很好,因为我想知道 Rubyist 解决这个问题的标准方法!

4

3 回答 3

4

那是因为您正在传递到注入块nil的情况。x[0] != x[1]这个块的返回值,是累加器值 ( sum) 的新值,因此,如果没有变化,只需 return sum。否则,新值sumis nilnil.respond_to(:+) #=> false在接下来的迭代中,会导致您遇到的错误。

n = 10
a = (1..n).to_a.product((1..n).to_a)
puts a.inject(0) {|sum, x| x[0] == x[1] ? sum + x[0] * x[1] : sum }
于 2013-05-26T08:07:15.727 回答
2
puts a.inject(0) {|sum, x| sum + x[0]*x[1] if (x[0] != x[1])}

在上面的 ruby​​ 语句中,如果条件 (x[0] != x[1]) 为假,则返回该值的 nil 并将其保存到“总和”中,因此下次如果条件为真,它将尝试向 sum 添加一些值,向 nil 添加一些值会引发此错误。

尝试阅读此文档。

http://ruby-doc.org/core-2.0/Enumerable.html#method-i-inject

于 2013-05-26T08:08:22.697 回答
2

我相信您知道, 的值sum是块在前一个表达式中评估的值(或第一次提供的初始值)

你的代码应该做的是

if x[0] != x[1]
  sum + x[0]*x[1]
else
  sum
end

您的代码有效地省略了 else,因此当不满足条件时,您的块评估为 0

您可能还想知道

(1..upto).combination(2).to_a

直接为您提供非重复对的数组,以便您不需要 if 语句注入

你甚至可以做

(1..upto).to_a.combination(2).collect {|pair| 2* pair[0] * pair[1]}.inject(:+)
于 2013-05-26T08:09:04.567 回答