Adding yet another Array.new form

  • Thread starter Nikolai Weibull
  • Start date
N

Nikolai Weibull

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

Lately I've been using the Kernel.returning method from Rails to write
it as

def a
returning ary = [] do
... construct ary by adding elements to it ...
end
end

which works quite well, but I think that I'd rather be writing it as

def a
Array.new do |ary|
... construct ary by adding elements to it ...
end
end

Array.new(size){ |index| block } would have to be changed so that size
is optional, and if not given, then this new way of creating an array
would be used. What do you people think? Is this worth yet another
RCR?,
nikolai
 
M

Martin DeMello

Nikolai Weibull said:
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,

I'd rather add another 'new' wrapper, that calls new, calls initialze,
and yields. Maybe call it 'create' or 'construct' or something.

Object,construct(*args) {|obj| .... }

The args would be passed to initialize, the block to construct. Would
fail if the defauly initializer needed a block, but perhaps there's a
way around that.

martin
 
R

Robert Klemme

Nikolai Weibull said:
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...

Kind regards

robert
 
N

Nikolai Weibull

Robert said:
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.

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

David A. Black

Hi --

Nikolai Weibull said:
Lately I've been using the Kernel.returning method from Rails to write
it as

def a
returning ary = [] do
... construct ary by adding elements to it ...
end
end
[...]

I'd rather vote in favor of including #returning to the Ruby core.
It is a very helpful method to DRY.

It feels a bit side-effect-dependent-ish, as written. It might be
nicer with a more traditional yield/block param syntax:

returning [] do |ary| ... end

That shows more of the separation between the fact that you're
returning an array, and the fact that you're assigning the name ary to
that array for use inside the block.


David
 
N

Nikolai Weibull

Christian said:
Nikolai Weibull writes:

[Array.new(size =3D nil){ block } suggestion]
I'd rather vote in favor of including #returning to the Ruby core.
It is a very helpful method to DRY.

Yes, it is. But I don=E2=80=99t see how including Kernel#returning makes=
the
addition of another form of Array.new pointless,
nikolai

--=20
Nikolai Weibull: now available free of charge at http://bitwi.se/!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}
 
K

Kirk Haines

def a
returning ary = [] do
... construct ary by adding elements to it ...
end
end
[...]

I'd rather vote in favor of including #returning to the Ruby core.
It is a very helpful method to DRY.

It feels a bit side-effect-dependent-ish, as written. It might be
nicer with a more traditional yield/block param syntax:

returning [] do |ary| ... end

That shows more of the separation between the fact that you're
returning an array, and the fact that you're assigning the name ary to
that array for use inside the block.

It should work either way. I haven't looked at the one in Rails, but mine:

module Kernel
def returning(*vals)
yield *vals
end
end


def a
returning tmp = [] do
tmp << 1; tmp << 2; tmp << 3
end
end

def b
returning [] do |ary|
ary << 1; ary << 2; ary << 3
end
end

irb(main):013:0> a
=> [1, 2, 3]
irb(main):014:0> b
=> [1, 2, 3]

And I agree with you. I like how the usage shown in b looks, myself, at least
in the places where I use this construction.


Kirk Haines
 
N

Nikolai Weibull

David said:
Nikolai Weibull writes:
Lately I've been using the Kernel.returning method from Rails to
write it as

def a
returning ary = [] do
... construct ary by adding elements to it ...
end
end
I'd rather vote in favor of including #returning to the Ruby core.
It is a very helpful method to DRY.
It feels a bit side-effect-dependent-ish, as written. It might be
nicer with a more traditional yield/block param syntax:

returning [] do |ary| ... end

That shows more of the separation between the fact that you're
returning an array, and the fact that you're assigning the name ary to
that array for use inside the block.

True. It's simple enough to write it that way as well. In fact,
implementing it as

module Kernel
def returning(value)
yield value
value
end
end

allows us to write it either way,
nikolai
 
J

Joel VanderWerf

Nikolai Weibull wrote:
...
module Kernel
def returning(value)
yield value
value
end
end
...

Why call it #returning and limit its usefulness? The method can be used
just as well in assignments:

ary = returning [] do ... end

or as an argument to a method or proc.

Maybe #configure, or #construct?

def configure(obj)
yield obj
obj
end

def m1
configure ary = [] do
ary << 1
ary << 2
end
end

p m1() # ==> [1, 2]

def m2
configure [] do |ary|
ary << 1
ary << 2
end
end

p m2() # ==> [1, 2]

I've never used that, but in some cases where there is a parent-child
relationship (or a sibling relationship), I've used a #create method, as
in the following:

def create(*args)
klass = args.shift
obj = klass.new(*args)
# maybe some additional args or setup based on
# relation between self and obj
yield obj
# register obj in self somehow
obj
end

def m3
create Array, 5 do |ary|
ary[0] = 1
ary[1] = 2
end
end

p m3() # ==> [1, 2, nil, nil, nil]
 
N

Nikolai Weibull

Kirk said:
module Kernel
def returning(*vals)
yield *vals
end
end

That'd return the last value of the block, not the value passed to
returning.

Also, the returning "function" is apparently also known as the K
combinator (there are many other interesting combinators in lambda
calculus, such as the Y combinator; see, for example,
http://en.wikipedia.org/wiki/Y_combinator) and can
be found in
vendor/rails/activesupport/lib/active_support/core_ext/kernel.rb in the
Rails distribution,
nikolai
 
K

Kirk Haines

That'd return the last value of the block, not the value passed to
returning.

Yeah. I was typing from memory and I left off that important little last bit.

Ah well. My point was yours. It's easy to implement and works both ways.


Kirk Haines
 
D

David A. Black

Hi --

def a
returning ary = [] do
... construct ary by adding elements to it ...
end
end
[...]

I'd rather vote in favor of including #returning to the Ruby core.
It is a very helpful method to DRY.

It feels a bit side-effect-dependent-ish, as written. It might be
nicer with a more traditional yield/block param syntax:

returning [] do |ary| ... end

That shows more of the separation between the fact that you're
returning an array, and the fact that you're assigning the name ary to
that array for use inside the block.

It should work either way. I haven't looked at the one in Rails, but mine:

module Kernel
def returning(*vals)
yield *vals
end
end


def a
returning tmp = [] do
tmp << 1; tmp << 2; tmp << 3
end
end

That only works by coincidence: tmp << 3 happens to return tmp :) If
anything else is the last expression in your block, you'll get a
different result:

def a
returning tmp = [] do
tmp << 1; tmp << 2; tmp << 3
puts "I've finished inserting into tmp!"
end
end

a # => nil, because it returns the value of the puts statement

#returning has to explicitly return the thing it yields; otherwise it
will return whatever the block it yielded to returns.


David
 
D

David A. Black

Hi --

Nikolai Weibull wrote:
...
module Kernel
def returning(value)
yield value
value
end
end
...

Why call it #returning and limit its usefulness? The method can be used
just as well in assignments:

ary = returning [] do ... end

or as an argument to a method or proc.

It's still "returning []" in the sense that it's going to return its
argument, even if the block's last expression does not evaluate to
that argument. It's #returning whose return value "returning obj"
refers to. You can, but do not have to, use it as the return value of
a method in which it appears.

For example:

def x
ary = returning [] do |a|
a.concat %w{a b c d e}
puts "Done with this block!"
end
p ary
return 100 # or whatever
end

x
=> Done with this block!
["a", "b", "c", "d", "e"]

Sidenote: it's also interesting how close this gets to:

[].instance_eval {|ary| ... }

(though obviously not identical). I can't think of a good name for:

[].yield_me_and_return_me {|ary| ... }

though :)


David
 
R

Robert Klemme

Nikolai said:
Robert said:
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:
=> [1, 2]

Kind regards

robert
 
N

Nikolai Weibull

Robert said:
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.

Yes, yes, but as you say, you still need the enumerable collection.
In this case I'd prefer the general approach that has been suggested
already:
=> [1, 2]

Again, this is a little more drastic than adding a line or two to the
initializer of Array for handling a case where the size argument is nil.
Sure, I think that a create method would have its uses, but that doesn't
mean that we can't have a slightly more flexible Array.new as well,
nikolai
 
N

Nikolai Weibull

David said:
Sidenote: it's also interesting how close this gets to:

[].instance_eval {|ary| ... }

(though obviously not identical).
True.

I can't think of a good name for:

[].yield_me_and_return_me {|ary| ... }

though :)

I was thinking of "build", but that's perhaps not very good,
nikolai
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top