Ruby语言中的Enumerable::inject方法
在使用一组数据的过程中(比如说数组),我们可能经常需要遍历其中的每一个元素,拿着每一个元素做些计算并收集这些计算结果,比如像下面这个数组:
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" |