温故而知新,可以为师矣

标签‘inject’下的内容

Ruby语言中的Enumerable::inject方法

作者:admin 时间:12.18.2009 类别:Ruby & Rails

在使用一组数据的过程中(比如说数组),我们可能经常需要遍历其中的每一个元素,拿着每一个元素做些计算并收集这些计算结果,比如像下面这个数组:

1
nums = [1, 3, 5, 7]

如果你想计算数组中所有元素的和,那么我们可以使用each方法,像下面这样:

1
2
sum = 0
nums.each { |n| sum += n }

这样子做是可以正常工作的,但是ruby提供了更灵活的inject方法,点这里查看(Enumerable::inject

1
sum = nums.inject(0) { |x,n| x+n }

上面这做对代码也可以计算出同样的值,但是它是用inject方法。Inject是通过一个accumulator来完成收集值的过程的,在上面代码中accumulator是x。inject的每一步中,上一次block执行的结果会被赋值给下一次block执行时的accumulator。在这里我们是把accumulator的初始值设定成了0,每一步中当前遍历的元素的值被加到了accumulator上。

如果省略了inject方法的参数,那么accumulator的初始值则是数组中的第一个元素,紧接着的遍历过程则从数组中第二个元素开始(下标为1),比如:

1
sum = nums.inject { |x,n| x+n }

它和下面代码完成的事情是一样的:

1
2
sum = nums[0]
nums[1..-1].each { |n| sum += n }

我们再看一个例子,假设我们有五个名子组成的数组:

1
names = ["edde", "rita", "guapo", "andy", "ash"]

下面的代码:

1
string = names.inject("") { |x,n| x << "#{n} " }

将会输出:

1
"edde rita guapo andy ash "

当然你也可以通过这样的代码来得到同样的结果:

1
2
string = ""
names.each { |n| string << "#{n} "}

这里有一点请大家记住,在inject的每一步里你可以做任何事情,或者说的更明确些你可以做非常复杂的事情,比如让我们上面的例子变得稍微复杂一些,我们想让每个人名首字母大写,后面接着一个逗号,人名和人名之间用空格分开,当然最后一个人名之后不需要再加任何字符,用我们熟悉的each方法,我们可以这样做:

1
2
3
4
5
string = ""
names.each do |n|
    name = n.length > 4 ? n.capitalize : n
    n == names.last ? string << name : string << "#{name}, "
end

如果用inject方法,可以像下面这样:

1
2
3
4
string = names.inject("") do |x,n|
  name = n.length > 4 ? n.capitalize : n
  n == names.last ? x << name : x << "#{name}, "
end

上面两段代码都会输入相同的结果:

1
"Edde, Rita, Guapo, Andy, Ash"
1 / 11