问:写一个方法,sum取一个数字数组并返回数字之和。
A:
def sum(nums)
total = 0
i = 0
while i < nums.count
total += nums[i]
i += 1
end
# return total
total
end
必须有另一种解决方法而不使用while,对吗?谁知道怎么样?
编辑:这不是考试或考试。这是应用学院的github上提供的练习题。他们以问题和答案为例。我只是读过那些优秀的程序员不喜欢使用的时候或者除非,所以我很好奇我是否可以学习一些东西以更好的方式解决这个问题。跟可数一样吗? (这里Ruby的Noob显然......)
另外,我会喜欢我应该学习的任何演练或方法。这个问题也不同,因为我要求使用这些数据的具体示例。
答案 0 :(得分:10)
通常的做法是:
def sum(nums) nums.reduce(&:+) end
这是这样的缩写:
def sum(nums) nums.reduce(0) { |total, num| total + num } end
我看到Neil在我输入时发布了类似的解决方案,所以我要注意reduce
和inject
是同一方法的两个名字 - Ruby有几个像这样的别名所以人们习惯于不同的其他语言可以找到他们正在寻找的东西。他还离开了&
,在reduce
/ inject
使用命名方法时是可选的,但在其他情况下则不行。
说明如下。
在Ruby中,您通常不使用显式循环(for
,while
等)。而是在您正在迭代的集合上调用方法,并为它们传递一个块代码以在每个项目上执行。 Ruby的语法将块放在方法的参数之后,在do
... end
或{
... }
之间,因此它看起来像传统的命令式流控制,但它的工作方式不同。
基本迭代方法是each
:
[1,2,3].each do |i| puts i end
调用块do |i| puts i end
三次,传递它1,然后传递它2,最后传递它3. |i|
是一个块参数,它告诉Ruby在哪里放置值( s)每次都进入区块。
但是each
只是抛弃了块调用的返回值(在这种情况下,nil
返回的三个puts
)。如果要对这些返回值执行某些操作,则可以调用其他方法。例如,map
返回一个返回值数组:
[1,2,3].map do |i| puts i end
#=> [nil, nil, nil]
这里不是很有趣,但如果块返回一些内容它会变得更有用:
[1,2,3].map do |i| 2*i end
#=> [2,4,6]
如果你想将结果合并到一个聚合返回值中,而不是返回一个与输入大小相同的数组,那就是当你到达reduce
时。除了一个块之外,它还需要一个额外的参数,并且块本身也会被一个额外的参数调用。这个额外的参数称为“累加器”。第一次调用块时,它获取最初传递给reduce
的参数,但从那时起,它获取上一次调用块的返回值,这是每个块调用可以将信息传递给下一个。
这使reduce
比map
更通用;实际上,您可以通过传入一个空数组并将块添加到map
来构建reduce
:
[1,2,3].reduce([]) do |a,i| a + [2*i] end
#=> [2,4,6]
但是由于已经定义了map
,你通常只会使用它,并且只使用reduce
来做更多,更好,还原的事情:
[1,2,3].reduce(0) do |s, i| s + 2*i end
#=> 12
...这就是我们在解决您的问题时所做的工作。
尼尔和我采取了一些额外的捷径。首先,如果一个块除了在其参数上调用单个方法并返回结果之外什么都不做,则可以通过在&:
前加上方法名称来获得等效块。就是这样:
some_array.reduce(x) do |a,b| a.some_method(b) end
可以更简单地重写:
some_array.reduce(x, &:some_method)
并且由于Ruby中的a + b
实际上只是编写方法调用a.+(b)
的一种更为熟悉的方式,这意味着您只需传入&:+
即可添加数字:< / p>
[1,2,3].reduce(0, &:+)
#=> 6
接下来,reduce
的初始累加器值是可选的;如果你把它留下来,那么第一次调用块时,它会得到数组的第一个两个元素。所以你可以不用0
:
[1,2,3].reduce(&:+)
#=> 6
最后,每当你传入一个不是文字块代码的块时,通常需要&
;您可以将块转换为Proc对象,并将它们存储在可以传递给不同迭代器方法的变量中。传递这样的变量时,通过将&
放在前面来指示它应该被解释为块而不是常规参数。
某些方法(包括reduce
)也会接受一个裸符号(如:+
)并为您创建Proc / block,而Neil利用了这一事实。但是其他迭代器方法(例如map
)不能以这种方式工作:
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>'
所以我总是使用&
。
irb(main):002:0> [-1,2,-3].map(&:abs)
#=> [1, 2, 3]
Ruby有很多很好的在线教程。有关map / reduce和相关概念的更多一般信息,以及如何将它们应用于解决问题,您应该搜索“函数式编程”的介绍,这被称为“函数编程”,因为它处理“函数”(即可执行块)代码,在Ruby中实现为Proc
个对象),就像数字和字符串一样,可以传递,分配给变量等。
答案 1 :(得分:7)
在Ruby中这样做最常用的方法可能是:
nums.inject(:+)
。 。 。虽然这基本上隐藏了所有工作,但这取决于测试试图测试的内容。