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:
Â
 a.each_with_index do |item,index|
Â
 end
Â
you can use this one:
Â
 a.each do |item,index|
Â
 end
Â
as well not disturbing this usage:
Â
 a.each do |item|
Â
 end
Â
This is my attempt to redefine Array#each but it does not work:
Â
class Array
 alias :old_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]
Â
irb(main):030:0> a.each do |item|
irb(main):031:1Â puts “item=#{item}”
irb(main):032:1> end
arity=1
item=1
item=2
item=3
=> [1, 2, 3]
Â
irb(main):033:0> a.each do |item,index|
irb(main):034:1Â puts “item=#{item}, index=#{index}”
irb(main):035:1> end
arity=2
arity=-1
=> [1, 2, 3]
Dan D. wrote:
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 :old_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
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.
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.
I am not sure why either but by aliasing each and each_with_index within
Array and tracing execution with some puts statements I learned that
each_with_index is implemented by calling each.
In any event, I used your code when arity=2 and the following now works
irb(main):028:0> a=[1,2,3]
=> [1, 2, 3]
irb(main):029:0>
irb(main):030:0* a.each do |item|
irb(main):031:1*Â Â puts “item=#{item}”
irb(main):032:1> end
item=1
item=2
item=3
=> [1, 2, 3]
irb(main):033:0* a.each do |item,index|
irb(main):034:1*Â Â puts “item=#{item} index=#{index}”
irb(main):035:1> end
item=1 index=0
item=2 index=1
item=3 index=2
I would like to learn how this same technique can be applied to hashes,
enums, & ranges and all enumerables.
This is my re-definition of Array:
class Array
 alias :old_each :each
 def each(&block)
   case block.arity
   when 1
     old_each &block
   when 2
     i = 0
     old_each do |curr|
       yield curr, i
       i+=1
     end
   end
 end
end
On Sep 10, 2:59 pm, “DanDiebolt.exe” [email protected] wrote:
… each_with_index and each depend on each other
Maybe someone with more knowlege can track this dependance down. I am keen to learn, but I have to admit that I am stretching my understanding of the Ruby language here.
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?
Can’t do it, it would break things.
I’ve always though an special ‘it’ object would be nice though. Eg.
[:a, :b, c:].each do |e|
it.index
it.first?
it.last?
end
T.
On Wed, Sep 10, 2008 at 7:53 PM, DanDiebolt.exe [email protected]
wrote:
arity=2
arity=-1
Where do you think the arity=-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} => #{b}” }
you see the arity of the block of each is indeed something which might
vary with the usecase.
HTH
Robert
C’est véritablement utile puisque c’est joli.
Antoine de Saint Exupéry
Thanks for pointing this out. I have two motivations to pursue this:
-
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.
-
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 …)?
2008/9/12 DanDiebolt.exe [email protected]:
to this code:
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