From: 7stud -- Date: 2009-09-16T03:35:00+09:00 Subject: Re: .map.with_object(3){|v|v+3} #=> 3 Is this a bug? David A. Black wrote: > Hi -- > > On Tue, 15 Sep 2009, 7stud -- wrote: > >> ------------------------------------------------- Enumerator#with_object >> e.with_object(obj) {|(*args), memo_obj| ... } >> e.with_object(obj) >> >> From Ruby 1.9.1 >> ------------------------------------------------------------------------ >> Iterates the given block for each element with an arbitrary object >> given, and returns the initially given object. >> >> If no block is given, returns an enumerator. > > That's pretty much what it does. It's kind of like inject, except it > uses the same object each time through instead of assigning the > accumulator slot to the value of the block. Thus: > > array = [1,2,3,4,5] > tens_hash = array.each_with_object({}) {|n,obj| obj[n] = n * 10 } > # => {1=>10, 2=>20, 3=>30, 4=>40, 5=>50} > > It saves you having to do that awkward: > > {|h,n| h[n] = n * 10; h } > > thing to feed the object back into the loop. > > Without a block, it returns an enumerator, which will then do a > "with"-style double iteration (like with_index) based on whatever you > call on it: > >>> array = [1,2,3,4,5] > => [1, 2, 3, 4, 5] >>> enum = array.each_with_object({}) > => # >>> tens_hash = enum.each {|n,obj| obj[n] = n * 10 } > => {1=>10, 2=>20, 3=>30, 4=>40, 5=>50} > > > David David A. Black wrote: > Hi -- > > On Tue, 15 Sep 2009, 7stud -- wrote: > >> ------------------------------------------------- Enumerator#with_object >> e.with_object(obj) {|(*args), memo_obj| ... } >> e.with_object(obj) >> >> From Ruby 1.9.1 >> ------------------------------------------------------------------------ >> Iterates the given block for each element with an arbitrary object >> given, and returns the initially given object. >> >> If no block is given, returns an enumerator. > > That's pretty much what it does. It's kind of like inject, except it > uses the same object each time through instead of assigning the > accumulator slot to the value of the block. Thus: > > array = [1,2,3,4,5] > tens_hash = array.each_with_object({}) {|n,obj| obj[n] = n * 10 } > # => {1=>10, 2=>20, 3=>30, 4=>40, 5=>50} > > It saves you having to do that awkward: > > {|h,n| h[n] = n * 10; h } > > thing to feed the object back into the loop. > > Without a block, it returns an enumerator, which will then do a > "with"-style double iteration (like with_index) based on whatever you > call on it: > >>> array = [1,2,3,4,5] > => [1, 2, 3, 4, 5] >>> enum = array.each_with_object({}) > => # >>> tens_hash = enum.each {|n,obj| obj[n] = n * 10 } > => {1=>10, 2=>20, 3=>30, 4=>40, 5=>50} > > > David >> Thanks for the response. In the definition: > e.with_object(obj) {|(*args), memo_obj| ... } 1) Why *args? 2) Why (*args) ? My tests show the first argument that with_object yields to the block may or may not be an array: example a: ---------- class A def my_iter yield "red", 1 yield "blue", 2 end end a = A.new a.my_iter{|x, y| puts "#{x} #{y}"} --output:-- red 1 blue 2 (That shows that the yield sends two arguments to the block--not an array.) h = {} results = e.with_object(h) do |args, _| p args end --output:-- ["red", 1] ["blue", 2] example b: --------- class A def my_iter yield 1 yield 2 end end a = A.new a.my_iter{|x| puts "#{x}"} --output:-- 1 2 e = a.enum_for(:my_iter) h = {} results = e.with_object(h) do |args, _| p args end --output:-- 1 2 ====== In the definition: > e.with_object(obj) {|(*args), memo_obj| ... } 3) Why 'memo_obj' and not 'obj'? Even though two different variable names makes it hard to track what is going on, the following assignment takes place: memo_obj = obj For example: e = a.enum_for(:my_iter) h = {} puts "#{h.inspect} #{h.object_id}" results = e.with_object(h) do |args, accumulator_hash| key, val = args accumulator_hash[key] = val print "#{accumulator_hash.inspect}" puts " #{accumulator_hash.object_id}" end --output:-- {} 345296 {"red"=>1} 345296 {"red"=>1, "blue"=>2} 345296 In the description: ------------------------------------------------- Enumerator#with_object e.with_object(obj) {|(*args), memo_obj| ... } e.with_object(obj) From Ruby 1.9.1 ------------------------------------------------------------------------ Iterates the given block for each element with an arbitrary object given, and returns the initially given object. If no block is given, returns an enumerator. ---------------------------------------------------- there needs to be a little more detail. Maybe something like: Yields element_of_e, obj to the block. args will be an array when e is attached to an iterator that yields more than one value, otherwise args will be the value the iterator yields. The return value of the block is discarded. If a block is given, with_object returns memo_obj. If no block is given, with_object returns an enumerator. Here's a full example that I played around with: class A def my_iter yield "red", 1 yield "blue", 2 end end a = A.new a.my_iter{|x, y| puts "#{x} #{y}"} --output:-- red 1 blue 2 e = a.enum_for(:my_iter) h = {} puts "#{h.inspect} #{h.object_id}" --output:-- {} 345212 results = e.with_object(h) do |args, accumulator_hash| p args key, val = args accumulator_hash[key] = val print "#{accumulator_hash.inspect}" puts " #{accumulator_hash.object_id}" 10 end --output:-- ["red", 1] {"red"=>1} 345212 ["blue", 2] {"red"=>1, "blue"=>2} 345212 -- Posted via http://www.ruby-forum.com/.