[ EDIT: All the example results/code (those with |a,b|.xyz in their block) are from test runs, not prediction. If they appear beneath a quote of "wouldn't it be useful if ...", the actual code to do the job is given. Yes, it rambles; but I get excited with toys. An RCR was not made. ] ++++++++++++++++++++++++++++++++++++++++++++++++++ Subject: |rcr|.xv Index Variables ( *_with_index ) From: "daz" Date: Tue, 2 Sep 2003 23:48:23 +0900 ++++++++++++++++++++++++++++++++++++++++++++++++++ Hello 'core, My first post here, so I'll try not to be too controversial. It might be too long for busy people. =begin [RCR] proposal - optional block index variable ============================================== * Applies to all iterators, including future additions. * No new methods. "with_index" depleted. * Problem of naming (map_with_indexes/indices) disappears. * No code breakage. "A Language shouldn't get in your way" * Removes an area where IMO it does. =end =begin Quoting David Black from http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/57500, If we had map_with_indices, you could do: myarray.map_with_indices {|e,i| e + ", " if i % 2 == 0}.compact but we don't. (Hint, hint.) End quote. myarray.map {|e|.i e + ", " if i % 2 == 0}.compact =end ================================================================= Ruby examples: ruby 1.8.0 (2003-08-30+) [i586-bccwin32] ================================================================= ----------8<----------------------------------------------------- a = ('a'..'z').to_a a.map! {|x|.n ' ' + x + (n+1).to_s if n % 2 == 0} a.display #-> a1 c3 e5 g7 i9 k11 m13 o15 q17 s19 u21 w23 y25 ----------8<----------------------------------------->8---------- loop {.x x < 5 or break p x } ----------8<----------------------------------------->8---------- =begin "each_byte_with_index" (etc.) doesn't exist. =end 'Hello'.each_byte {|ch|.ix puts ch} # index, but not used #-> 72 #-> 101 #-> 108 #-> 108 #-> 111 ----------8<----------------------------------------->8---------- # # Until each_with_index gets flushed # down the toilet, we could do ... # # each_with_index (with index) Mmm, sounds nice. (10..16).each_with_index do |n, wx|.index p [n, wx, index] raise 'Oh-Oh!' if index != wx end #-> [10, 0, 0] #-> [11, 1, 1] #-> [12, 2, 2] #-> [13, 3, 3] #-> [14, 4, 4] #-> [15, 5, 5] #-> [16, 6, 6] ----------8<----------------------------------------->8---------- 10.times {|n|.n n+1} #-> C:/TEMP/rbA115.TMP: 2: duplicate name for block parameter / index variable #-> 10.times {|n|.n n+1} #-> ^ ----------8<----------------------------------------->8---------- y = 10 flg = 'a' 2.times do | z2 |.x 3.times do | z3 |.y puts " #{z2}#{x}, #{z3}#{y}, (#{flg})" if [z2, x, z3, y, flg] == [1,1,1,1,'a'] flg = 'c' puts '* state change *' retry # retry/redo end puts '-'*15 end end puts "final y = #{y}" #-> 00, 00, (a) #-> --------------- #-> 00, 11, (a) #-> --------------- #-> 00, 22, (a) #-> --------------- #-> 11, 00, (a) #-> --------------- #-> 11, 11, (a) #-> * state change * #-> 11, 00, (c) #-> --------------- #-> 11, 11, (c) #-> --------------- #-> 11, 22, (c) #-> --------------- #-> final y = 2 =begin From: http://zem.novylen.net/ruby/withindex.rb module Enumerable def method_missing(meth, *args, &block) if meth.to_s =~ /_with_index/ m = meth.to_s.gsub!('_with_index','') i = -1 self.send(m,*args) {|n| i = i+1 block.call(n,i) } end end end =end =begin From: http://www.loveruby.net/~aamine/ja/tdiary/20021210.html # (extract of translation, edited) unless [ ].respond_to?(:Sort_by) class Array def sort_by list = [ ] each_with_index do |i, idx| list.push( [ yield(i), (idx and i) ] ) end list.sort.map { |tmp, (idx and i)| i } end end end # When doing such, the map_with_index # (or the Iterator) it becomes desired. # #(11:49) =end ----------8<----------------------------------------->8---------- # ============================================================ # Extension for experimentalists - $XITER (silly temp. name) # ============================================================ # You can use $XITER virtual_variable where block_given? applies. # (Safe anywhere. Effect will be correct, even if you disagree;) def x(q) 3.times do |i|.ix printf("\nIter #%i==%i (%i)\n", i, ix, $XITER) $XITER += 2 yield q, i, ix, $XITER end end x ('roo') do |yq, yi, yix, yiter|.xv p [[yq, yi, yix], [yiter, xv], $XITER] end #-> Iter #0==0 (0) #-> [["roo", 0, 0], [2, 2], false] #-> #-> Iter #1==1 (3) #-> [["roo", 1, 1], [5, 5], false] #-> #-> Iter #2==2 (6) #-> [["roo", 2, 2], [8, 8], false] ----------8<----------------------------------------->8---------- =begin Reasons why I left Ruby for another language ============================================ Ruby has powerful iterators but no access to their indexices. So I changed to Ruby, which has both! C OO L. Or should that be indicexes ??? Now my mind is free to explore inject(i) {|a,b|.x 'wheeesh'} History (for power readers) =========================== [CHANGELOG] Thu Feb 5 18:58:46 1998 Yukihiro Matsumoto * enum.c (enum_each_with_index): new method. ==== http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/56121 DAB - MWI, Inc. http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/56125 Matz - YAGNI http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/56719 Matz http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/56730 Matz http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/56845 _why http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/56939 extending block w/ counter =end ----------8<----------------------------------------->8---------- # Currently difficult ? "Hello World!".scan(/[aeiou]./) do |str|.ix printf("(%2s) @ %2d\n", str, ix) end #-> (el) @ 1 #-> (o ) @ 4 #-> (or) @ 7 ----------------------------------------------------->8---------- ================================================================= Implementation Notes ================================================================= 1) The index variable (xv) is recognised by the parser in a similar way to a block parameter. 2) An assignment node is created (L/DASGN) depending on whether the xv already exists in an outer scope. 3) When an ITER node is created by the parser and an xv has been recognised, pointers to the ASGN node and the ITER node are stored in an ITERX node. (It would have been easier to extend NODE_ITER by the size of a pointer but that could, possibly, extend all ruby objects.) 3a) Without xv: (Normal Ruby) NODE_ITER u1 'nd_var' (block params) u2 'nd_body' (block statements) u3 'nd_iter' (func; e.g. each) 3b) With xv: NODE_ITERX u1 'nd_inode' (-> ITER node) u2 'nd_xvasn' (-> L/DASGN) [would be in u4] u3 'nd_iter' (func; e.g. each) NODE_ITER (from 'nd_inode' of ITERX above) u1 'nd_var' (block params) u2 'nd_body' (block statements) u3 0 ================================================================= 4) When rb_eval sees an ITERX node and the struct BLOCK is established, a pointer to the ASGN node is set in the block for easy access and is used in tests for xv presence. (Access to the ASGN node via the ITERX node may be lost after a ruby 'redo' / 'retry' etc. Access is maintained via the pointer in the BLOCK.) [struct BLOCK is necessarily extended by 4 bytes.] The value (index count) in the ASGN node is initialised to zero at this time. 5) On a yield to the block (rb_yield_0), the value in the ASGN node (which also holds the variable's ID) is assigned to the index variable. 6) Before rb_yield_0 returns, the index count is incremented but will be assigned to the variable only at the start of the next iteration. ================================================================= * Any assignment to the xv from a script will NOT be carried over to the next iteration. * Any iterator method implemented by the interpreter will be able to alter the index value before the next iteration. For example, String#scan could send the index position of each match (currently inaccessible ?) into the block, if an xv has been supplied, simply by overwriting the new index value before a yield, using ... void rb_xv_assign_long(long); String#scan is the only iterator method that has been tampered with. Unmodified, an xv in the block would increment from zero like any other but it seemed that selected methods might be usefully tweaked ? (e.g. gsub) ================================================================= Any need for this ?? (Perhaps in Japan ?) (Please pile on the pressure if you need similar fun ;) [xv.patch] attached for experiments. (CVS head 2003/09/02) (EDIT: OUT-OF-DATE !!!) Feel free to criticise but be forewarned that grumbling about syntax could result in public humiliation from a prepared, bullet-proof defence (defense) :-) [...] daz Thread continues at: http://www.ruby-talk.org/cgi-bin/vframe.rb/ruby/ruby-core/1496?1419-1650+split-mode-vertical