Redefe each to include each_with_index and arity=2

D

DanDiebolt.exe

Is is possible to redefine each so that each_with_index behavior can be ach=
eived with an extra parameter to the block each is called with? In other wo=
rds, how do you redefine each so that instead of using this construct:
=A0
=A0 a.each_with_index do |item,index|
=A0
=A0 end
=A0
you can use this one:
=A0
=A0 a.each do |item,index|
=A0
=A0 end
=A0
as well not disturbing this usage:
=A0
=A0 a.each do |item|
=A0
=A0 end
=A0
=3D=3D=3D=3D=3D=3D
This is my attempt to redefine Array#each but it does not work:
=A0
class Array
=A0 alias :eek:ld_each :each
=A0 def each(&block)
=A0=A0=A0 puts "arity=3D#{block.arity}"
=A0=A0=A0 case block.arity
=A0=A0=A0 when 1=20
=A0=A0=A0=A0=A0 old_each &block
=A0=A0=A0 when 2
=A0=A0=A0=A0=A0 each_with_index &block
=A0=A0=A0 end
=A0 end
end
=A0
a=3D[1,2,3]
=A0
irb(main):030:0> a.each do |item|
irb(main):031:1*=A0 puts "item=3D#{item}"
irb(main):032:1> end
arity=3D1
item=3D1
item=3D2
item=3D3
=3D> [1, 2, 3]
=A0
irb(main):033:0> a.each do |item,index|
irb(main):034:1*=A0 puts "item=3D#{item}, index=3D#{index}"
irb(main):035:1> end
arity=3D2
arity=3D-1
=3D> [1, 2, 3]
 
A

Anton Ivanov

Dan said:
Is is possible to redefine each so that each_with_index behavior can be
acheived with an extra parameter to the block each is called with? In
other words, how do you redefine each so that instead of using this
construct:
======
This is my attempt to redefine Array#each but it does not work:
 
class Array
  alias :eek:ld_each :each
  def each(&block)
    puts "arity=#{block.arity}"
    case block.arity
    when 1
      old_each &block
    when 2
      each_with_index &block
    end
  end
end
 
a=[1,2,3]
 

I'm not exactly sure why your code does not work. It seems that
each_with_index and each depend on each other in ways which you are not
capturing with your redefined each. But you can roll your own index
tracking:

def each(&block)
i = 0
arity = block.arity
old_each do |curr|
case arity
when 1: yield curr
else yield curr, i
end
i+=1
end
end
 
A

Anton Ivanov

I would like to ask others if they think having each overloaded to take
either |item| or |item,index| would be a general language improvement.
Also, for greatest generality should this proposed redefinition of each
be made to the class Array or to the enumerator mixin or somewhere else?

Like you, I've wondered why each could not do both things. On the one
hand it would be nice if it supported the index functionality. However,
it is really a breaking change. Consider what a and b are assigned
currently at each iteration of the following, and what they would be
assigned under your proposal.

[[1,2], [3,4]].each {|a,b|}

To answer your second question, each() must know the details of the
container, and so it cannot be in a mixin. The Enumerable mixin defines
functionality in terms of 'each' which must be supplied by the class
into which you mix it in.
 
R

Robert Dober

arity=3D2
arity=3D-1

Where do you think the arity=3D-1 does come from? Does this ring a bell?

Actually each_with_index will use a block with *args calling each again.
Now that brings us to why your idea might not be as good as you
thaught at first sight:

[[:a, 42], [:b,43]].each{ |a,b| puts "#{a} =3D> #{b}" }

you see the arity of the block of each is indeed something which might
vary with the usecase.

HTH
Robert
--=20
C'est v=E9ritablement utile puisque c'est joli.

Antoine de Saint Exup=E9ry
 
R

Robert Klemme

2008/9/12 DanDiebolt.exe said:
Thanks for pointing this out. I have two motivations to pursue this:

1) I am interested in more deeply learning all of ruby language constructs but I only occasionally write my own classes & iterators. I would like to get the the point where I can do this with ease.

2) As a practical matter I often have to modify code I wrote a while ago that was originally thrown together for a quick solution to a one of a kind problem. So I like to use editing techniques that can quickly modify code without of a lot of rework. It may seem trivial, but is sure seems to me that adding an index parameter to the each iterator is a lot easier than changing the method to each_with_index AND adding an index parameter. In other words, moving from this code:

a.each do |item|
#process item
end

to this code:

a.each do |item,index|
#process item & index
end

is easier to remember and edit than using this code:

a.each_with_index do |item,index|

#process item & index

end

So if Ruby is such a great dynamic language and DSL (I know it is!) there should be a straightforward way to accomplish this despite the *args problem you mention.

So does anyone know how each could be expanded in this fashion to work for all enumerable types (ranges, hashes, sets ...)?

I'd rather discourage doing this. If you edit source code to add
another block parameter then it seems equally easy to replace "each"
with "each_with_index".

Note that evaluating the arity of the block has some issues because
for a Hash you usually provide two arguments as in

a_hash.each do |key, value| # arity 2
print key, " ---> ", value, "\n"
end

or one argument as in

a_hash.each do |*pair| # arity -1
p pair
end

And even to an each_with_index for a Hash you can reasonably provide
two (!) arguments:

irb(main):007:0> def ar(&b) b.arity end
=> nil
irb(main):008:0> {"a"=>"b"}.each_with_index {|(k,v),i| p k,v,i}
"a"
"b"
0
=> {"a"=>"b"}
irb(main):009:0> ar {|(k,v),i| p k,v,i}
=> 2

Kind regards

robert
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top