Ruby 中的 Array.first 与 Array.shift

Mai*_*kon 5 ruby arrays methods shift

我本质上正在经历一个练习,从 Enumerable 模块重写注入方法的基本形式,而我的解决方案没有做任何事情,因为我使用的是#first:

def injecting(*acc, &block)
  acc = acc.empty? ? self.first : acc.first
  self.each do |x|
     acc = block.call(acc, x)
  end  
  acc
end
Run Code Online (Sandbox Code Playgroud)

然后我遇到了另一个解决方案,它使用 #shift 而不是 #first 并且工作得很好:

def injecting(*acc, &block)
  acc = acc.empty? ? self.shift : acc.first
  self.each do |x|
    acc = block.call(acc, x)
  end  
  acc
end  
Run Code Online (Sandbox Code Playgroud)

我知道 #first 返回数组的第一个元素,但不会更改它,而 #shift 返回并更改它,但我很难理解如果要进行变异,下面的代码如何仍然获得所需的结果通过删除第一个元素来删除数组:

[1,2,3].injecting { |a,x| a + x } # --> 6
Run Code Online (Sandbox Code Playgroud)

任何智慧之言将不胜感激。

谢谢!

Aru*_*hit 4

只需添加一行p acc即可调试它。

(arup~>~)$ pry --simple-prompt
>> module Enumerable
 |   def injecting(*acc, &block)
 |     acc = acc.empty? ? self.shift : acc.first  
 |     p acc  
 |     self.each do |x|  
 |       acc = block.call(acc, x)    
 |     end      
 |     acc  
 |   end    
 | end  
=> nil
>> [1,2,3].injecting { |a,x| a + x } 
1
=> 6
>> 
Run Code Online (Sandbox Code Playgroud)

injecting当您调用数组上的方法时[1,2,3],因为acc数组是空的,所以sef.shift会被调用。现在acc1。现在self只有[2,3]. 因此,self.each..首先调用2,然后调用块,并将 的结果a = x分配给acc,现在保持不变3。所以现在在下一次迭代中,从self下一个值传递(3)开始,块再次被调用,并a + x再次执行,所以现在的acc值为6。所以你得到了结果6

多一级调试:

(arup~>~)$ pry --simple-prompt
>> module Enumerable
 |   def injecting(*acc, &block)
 |     acc = acc.empty? ? self.shift : acc.first  
 |     p "acc now holds #{acc}"  
 |     p "elements in self is #{self}"  
 |     self.each do |x|  
 |       acc = block.call(acc, x)    
 |       p "current value of acc is #{acc}"    
 |     end      
 |     acc  
 |   end  
 | end  
=> nil
>> [1,2,3].injecting { |a,x| a + x } 
"acc now holds 1"
"elements in self is [2, 3]"
"current value of acc is 3"
"current value of acc is 6"
=> 6
>>
Run Code Online (Sandbox Code Playgroud)

根据OP的评论

我可以安全地假设通过首先调用,仍然返回第一个项目,但从该项目开始迭代,因此为什么我会得到 7?

我再次建议您添加一些调试消息,以查看底层发生了什么。往下看 :

(arup~>~)$ pry --simple-prompt
>> module Enumerable
 |   def injecting(*acc, &block)
 |     acc = acc.empty? ? self.first : acc.first  
 |     p "acc now holds #{acc}"  
 |     p "elements in self is #{self}"  
 |     self.each do |x|  
 |       acc = block.call(acc, x)    
 |       p "current value of acc is #{acc}"    
 |     end      
 |     acc  
 |   end  
 | end  
=> nil
>> [1,2,3].injecting { |a,x| a + x }
"acc now holds 1"
"elements in self is [1, 2, 3]"
"current value of acc is 2"
"current value of acc is 4"
"current value of acc is 7"
=> 7
>> 
Run Code Online (Sandbox Code Playgroud)

self.first只返回第一个元素,即1,并被分配给acc。但 self 没有被修改。但万一self.shift,它1被分配到acc并同时被删除self,然后self就有了[2,3]

现在在这部分中,self.each..代码传递 的 3 个值self,分别是123。现在总和是6,并与 相加,这是调用时的1第一个acc值。self.first最终的结果就是这样acc7