1

我目前正在学习 ruby​​,我写了这段代码:

def multi_gen
  s = []
  for i in (3..10)
    if i%3 == 0 || i%5 == 0
      s<<i
    end
  end
  return s
end

puts multi_gen

def rec_sum(num_arr)
  if num_arr == []
    return 0
  else
    num_arr.first + rec_sum(num_arr.shift)
  end
end

puts rec_sum(multi_gen)

这应该返回所有 3 和 5 倍数的总和,最高为 1000。

但我得到一个错误:

myrbfile.rb:17:in `rec_sum': undefined method `first' for 3:Fixnum (NoMethodError)
        from villani.rb:17:in `rec_sum'
        from villani.rb:21:in `<main>'

但是当我像这样重写它时:

def multi_gen
  s = []
  for i in (3..10)
    if i%3 == 0 || i%5 == 0
      s<<i
    end
  end
  return s
end

puts multi_gen

def rec_sum(num_arr)
  if num_arr == []
    return 0
  else
    num_arr[0] + rec_sum(num_arr[1..num_arr.last])
  end
end

puts rec_sum(multi_gen)

我没有得到错误。

那么为什么我的第一个 rec_sum 函数在第一种情况下将我的 Array 解释为 Fixnum 呢?

4

2 回答 2

2

问题在于递归调用:

rec_sum(num_arr.shift)

Array#shift返回移位的元素,而不是剩余的数组。您应该将数组作为参数显式传递给递归调用:

rec_sum(num_arr[1..-1])

或者

rec_sum(num_arr.tap(&:shift))

后者 [可能] 对于初学者来说看起来太麻烦了,但这是一种非常常见的 ruby​​ish 方法:Object#tap将接收器让给块,返回接收器。在块内 (num_arr.tap(&:shift)num_arr.tap { |a| a.shift }我们通过将元素移出来改变数组的简写,它会作为结果返回。

于 2018-04-10T07:54:46.823 回答
0

mudasobwa已经解释了为什么 usingshift没有给出预期的结果。除此之外,您的代码在某种程度上是单调的。

multi_gen您创建一个空数组并使用for循环将元素附加到它。您很少需要手动填充数组。相反,您通常可以使用 Ruby 的一种ArrayEnumerable方法来生成数组。select是一个非常常见的 - 它返回一个数组,其中包含给定块返回的元素true

(1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }
#=> [3, 5, 6, 9, 10, 12, ...]

rec_sum,你检查if num_arr == []。虽然这可行,但您正在创建一个空的一次性数组。要确定一个数组是否为空,你应该调用它empty?

if num_arr.empty?
  # ...
end

要从数组中获取剩余元素,请使用:

num_arr[1..num_arr.last]

可以通过将负索引传递给来缩写[]

num_arr[1..-1]

还有drop可能看起来更好一点:

num_arr[0] + rec_sum(num_arr[1..-1])
# vs
num_arr.first + rec_sum(num_arr.drop(1))

从数组中获取第一个和剩余元素的另一个选择是 Ruby 的数组分解功能(注意*):

def rec_sum(num_arr)
  if num_arr.empty?
    0
  else
    first, *remaining = num_arr
    first + rec_sum(remaining)
  end
end

您还可以考虑使用保护子句提前从方法返回:

def rec_sum(num_arr)
  return 0 if num_arr.empty?
  first, *remaining = num_arr
  first + rec_sum(remaining)
end

编写递归方法非常适合用于学习目的,但 Ruby 也有一个内置sum方法:

multi_gen.sum #=> 234168

或者——因为你使用的是旧的 Ruby 版本—— inject

multi_gen.inject(0, :+) #=> 234168
于 2018-04-10T08:59:28.320 回答