문제

I'm essentially going through an exercise for rewriting a basic form of the inject method from the Enumerable module and my solution was not doing anything as I was using #first:

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

I then came across another solution which was using #shift instead of #first and was working just fine:

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

I know that #first returns the first element of the array but doesn't alter it while #shift returns and alters it, but I'm having a hard time understanding how the below code would still get the desired result if you were to mutate the array by dropping the first element:

[1,2,3].injecting { |a,x| a + x } # --> 6

Any words of wisdom would be greatly appreciated..

Thanks!

도움이 되었습니까?

해결책

Just add one line p acc to debug it.

(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
>> 

When you called the method injecting on the array [1,2,3], as acc was empty, sef.shift gets invoked. Now acc is 1. Now self has only [2,3]. so the call self.each.. first pass 2, and then block gets invoked and the result of a = x is assigned to acc, whis now holds 3. So now on the next iteration, from self next value passed(3), and block gets called again, and perform a + x again, so now value of acc is 6. So you got the result 6.

A one level more debugging :

(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
>>

As per OP's comment

Am I safe assuming that by calling first, is still returning the 1st item but starts the iteration from that item, hence why I would get 7?

Again I would suggest you to add some debugging messages, to see what's going go under-hood. Look below :

(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
>> 

self.first just returns the first element, which is 1, and get assigned to acc. But self didn't get modified. But in case of self.shift, that 1 was assigned to acc and at the sane time got removed from self, and then self has [2,3].

Now in this part, self.each.. code passes 3 values of self, which are 1,2 and 3. Now the summation is 6, and added with 1, which is the first acc value, when self.first got called. This is how the final result of acc is 7.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top