David A. Black wrote:
[...]
I don't have any definitive answers, but I think there's room to clear
up the ambiguous status of the concept "index" as it is manifested in
Enumerable.
In history, there was a hack submitted to ruby-core which tried to
implement a "block index" which supplied an automatically incrementing
counter to each invocation of *any* iterator.
The discussion turned into one about syntax rather than the concept.
Obviously, when the interpreter is hacked, some syntax might have to be
chosen to expose the index accessor.
I chose for myself (which is the author's prerogative and kind of essential).
So the analogue of this snippet:
[1,2,3,4,5,6].map_with_index! do |e, ix|
[2, 5].include?(ix) ? e : e*2
end
was proposed to be:
[1,2,3,4,5,6].map! do |e|.ix
[2, 5].include?(ix) ? e : e*2
end
In short, #each_with_index is scrapped and you use the standard #each
(or #map or any of the other methods taking a block) with an index/counter
magically available, on demand, within the block.
One could implement a fixed name like '_index_' or one of those Perl
thingies beginning with $ having local scope (as $1, $2 etc.) or any
number of alternatives; the principle is the same.
Two typically useful applications are:
HUGE_ARRAY.each do |elem|.elnum
break if $DBG && elnum > 500 # seen enough
# stuff
end
IO.foreach() do |line|.linum
# stuff where line number is useful (duh!)
end
One major factor that has bugged me is the lack of symmetry
between #each and #e_w_i which discourages temporary changes.
h = {:a => 1, :b => 2, :c => 3}
# Current conversion of Hash#each to #each_with_index
h.each {|ek, ev| p [ek , ev ]}
h.each_with_index {|e, idx| p [e[0], e[1], idx]}
# ^_________^
^^_^ 
# A B C D C E
# A. Change the method name to a much longer one.
# B. Name the index variable.
# C. Hash#each gives key and value -- #each_with_index
# passes these as a two-element array into the first
# block parameter. Specify which element.
# D. The name you chose for the second block parameter of each
# will, probably, need changing (if you used, say, 'key, val').
# E. Use the index variable.
Compared to this (with a wishful block index variable):
h.each {|ek, ev| p [ek, ev ]}
h.each {|ek, ev|.xv p [ek, ev, xv]}
#
^_
# B E
Name it ... use it. (=== *_with_index)
In the interpreter, there'd be an integer in each BLOCK
struct which gets incremented before each iteration.
Not too expensive ?
One final gripe -- if you need a counter inside a block,
it's quicker to maintain your own than use #e_w_i.
(Do reply if this is incorrect.)
···
#--------------------------------------
require 'benchmark'
include Benchmark
arr = Array.new(100000)
bm(7) do |x|
x.report('eachA') do
ixj = 0; arr.each {|e| ixj += 1}
end
x.report('e_w_i') do
arr.each_with_index {|e, dmy| dmy}
end
x.report('eachB') do # same as 'eachA'
ixj = 0; arr.each {|e| ixj += 1}
end
end
#--------------------------------------
BTW, I had to remove my local patch because it was sooo
usable that it was integrating into my style.
daz