关于ruby:数组的总和

Sum array of numbers

本问题已经有最佳答案,请猛点这里访问。

Q:写一个方法,求和,它取一个数字数组,并返回数字的和。

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
def sum(nums)
  total = 0

  i = 0
  while i < nums.count
    total += nums[i]

    i += 1
  end

  # return total
  total
end

必须有另一种不使用while的方法来解决这个问题,对吗?有人知道怎么做吗?

编辑:这不是考试。这是Github为应用学院提供的实践问题。他们以问题和答案为例。不过,我刚刚读到,优秀的程序员不喜欢使用while或until,所以我很好奇是否可以学习一些更好的方法来解决这个问题。像是可枚举的?(显然,这里没有红宝石…)

另外,我也很喜欢任何我应该学习的演练或方法。这个问题也是不同的,因为我在询问使用这些数据的具体示例。


通常的做法是:好的。

1
def sum(nums) nums.reduce(&:+) end

它的缩写是:好的。

1
def sum(nums)  nums.reduce(0) { |total, num| total + num } end

我看到Neil在我打字的时候发布了一个类似的解决方案,所以我要注意的是,reduceinject是同一方法的两个名称-ruby有几个类似的别名,这样,使用不同语言的人就可以找到他们想要的东西。他还留下了&,当对reduce/inject使用命名方法时,这是可选的,但在其他情况下则不是。好的。

解释如下。好的。

在Ruby中,通常不使用显式循环(forwhile等)。相反,您可以在要迭代的集合上调用方法,并向它们传递要在每个项上执行的代码块。Ruby的语法将块放在方法的参数之后,在doend{}之间,因此它看起来像传统的命令流控制,但工作方式不同。好的。

基本迭代法为each:好的。

1
[1,2,3].each do |i| puts i end

这三次调用块do |i| puts i end,将其传递1,然后传递2,最后传递3。|i|是一个块参数,它告诉ruby每次将传递到块中的值放在哪里。好的。

但是each只是丢弃了块调用的返回值(在本例中,puts返回的三个nil)。如果要对这些返回值执行某些操作,则需要调用其他方法。例如,map返回一个返回值数组:好的。

1
2
[1,2,3].map do |i| puts i end
#=> [nil, nil, nil]

这在这里不是很有趣,但是如果块返回某些内容,它会变得更有用:好的。

1
2
[1,2,3].map do |i| 2*i end  
#=> [2,4,6]

如果您希望将结果组合成单个聚合返回值,而不是返回与输入大小相同的数组,这时您需要到达reduce。除了块之外,它还需要一个额外的参数,并且块本身也用一个额外的参数来调用。这个额外的参数称为"累加器"。第一次调用块时,它会得到最初传递给reduce的参数,但从那时起,它会得到上一次调用块的返回值,这就是每个块调用如何将信息传递给下一个块。好的。

这使得reducemap更通用;实际上,您可以通过传入一个空数组并在其中添加块来从reduce中构建map:好的。

1
2
[1,2,3].reduce([]) do |a,i| a + [2*i] end
#=> [2,4,6]

但是,由于map已经定义好了,所以通常只使用它,只使用reduce来做更简单的事情:好的。

1
2
[1,2,3].reduce(0) do |s, i| s + 2*i end  
#=> 12

…这就是我们为解决你的问题所做的。好的。

尼尔和我多走了几条捷径。首先,如果一个块只对其参数调用一个方法并返回结果,那么您可以通过在方法名前面加上&:得到一个等价的块。也就是说,这:好的。

1
some_array.reduce(x) do |a,b| a.some_method(b) end

可以更简单地重写为:好的。

1
some_array.reduce(x, &:some_method)

而且因为Ruby中的a + b实际上只是一种更熟悉的方法来编写名为a.+(b)的方法,这意味着您可以通过传入&:+来相加:好的。

1
2
[1,2,3].reduce(0, &:+)
#=> 6

接下来,reduce的初始累加器值是可选的;如果不使用它,那么第一次调用块时,它会得到数组的前两个元素。所以你可以不使用0:好的。

1
2
[1,2,3].reduce(&:+)
#=> 6

最后,在传递一个不是文本代码块的块时,通常需要&;您可以将块转换为proc对象,并将其存储在变量中,以便传递给不同的迭代器方法。当传递这样的变量时,通过将&放在它前面,您指示应该将它解释为块而不是常规参数。好的。

一些方法,包括reduce,也会接受一个空符号(如:+)并为您创建proc/block,Neil利用了这个事实。但是其他迭代器方法(如map方法)不能这样工作:好的。

1
2
3
4
5
irb(main):001:0> [-1,2,-3].map(:abs)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):1:in `map'
from (irb):1
from /usr/bin/irb:12:in `
<main>'

所以我总是使用&。好的。

1
2
irb(main):002:0> [-1,2,-3].map(&:abs)
#=> [1, 2, 3]

Ruby有很多很好的在线教程。有关map/reduce和相关概念以及如何将它们应用于解决问题的更多一般信息,您应该搜索"函数编程"的介绍,之所以称之为"函数编程",是因为它将"函数"(即在Ruby中实现为Proc对象的可执行代码块)视为与数字和字符串,可以传递、分配给变量等。好的。好啊。


在Ruby中,最惯用的方法可能是:

1
nums.inject(:+)

. …虽然这基本上隐藏了所有的工作,所以这取决于测试试图测试什么。

数组注入文档