From: Robert Klemme Date: 2005-08-08T18:41:08+09:00 Subject: Re: Adding yet another Array.new form Nikolai Weibull wrote: > Robert Klemme wrote: > >> Nikolai Weibull wrote: > >>> How about adding a fifth way of constructing new arrays?: >>> >>> Array.new{|array| block } >>> >>> Here, array is the newly created array, and the block will >>> initialize it as it sees fit, e.g, >>> >>> ary = Array.new{ |ary| 5.times{ |i| ary << i } }, >>> >>> which is a stupid way of doing it (Array.new(5){ |i| i } would do), >>> but it shows the idea. I often find myself writing methods that fit >>> the following template: >>> >>> def a >>> ary = [] >>> .. construct ary by adding elements to it ... >>> ary >>> end > >> But *what* are the initializations you cannot do nicely ATM? Note, >> that there are also forms that make use of map and inject that can be >> quite concise... > > Huh? Array#map and Array#inject operate on an array to produce > another array or value. What I'm suggesting is a way to set up the > items of an array in a nice way. They are methods in Enumerable. map returns an array and inject can be used to return an array: >> (1..10).map{|x| x + 0.5} => [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5] >> (1..10).inject([]){|a,x| a << x + 0.5} => [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5] If you need to create an Array from something enumerable these are quite conveninent. > (Actually, as other people have pointed out on this thread, this isn't > necessary limited to arrays, but I'd say it's most applicable to the > initialization of collections.) > > Consider the following example from my ruby-lisp format rewrite: > > def parse_modifiers > returning modifiers = [] do > until @format.eof? > if (c = @format.getc) == ?: or c == ?@ > if modifiers.include? c > raise FormatError.new(@format.pos, "duplicate #{c.chr} > modifier") end > modifiers << c > else > @format.ungetc(c) > break > end > end > end > end > > Without using returning, that would be > > def parse_modifiers > modifiers = [] > until @format.eof? > if (c = @format.getc) == ?: or c == ?@ > if modifiers.include? c > raise FormatError.new(@format.pos, "duplicate #{c.chr} > modifier") end > modifiers << c > else > @format.ungetc(c) > break > end > end > modifiers > end > > I would like to be able to write it as > > def parse_modifiers > Array.new do |modifiers| > until @format.eof? > if (c = @format.getc) == ?: or c == ?@ > if modifiers.include? c > raise FormatError.new(@format.pos, "duplicate #{c.chr} > modifier") end > modifiers << c > else > @format.ungetc(c) > break > end > end > end > end > > I feel that this makes it clear that parse_modifiers returns a new > array and then the block passed to Array.new sets it up as > appropriate. > There's already support for passing a block to initialize an array, > but that can only be done in the following manner: > > Array.new(5){ |i| i } # => [0, 1, 2, 3, 4] > > or > > Array.new(5){ |i| i * 2 } # => [0, 2, 4, 6, 8] > > Sure, the format I'm suggesting is perhaps limited in applicability, > but how often do you do Array.new(size){ |index| ... }? I have four > methods in one class whose bodies are a big returning, like > parse_modifiers > above, that sets up an array and then returns it, > nikolai In this case I'd prefer the general approach that has been suggested already: >> class Class >> def create(*a) >> obj = new(*a) >> yield obj >> obj >> end >> end => nil >> Array.create do |ar| ?> ar << 1 << 2 >> end => [1, 2] Kind regards robert