How does one access the array index in #map?
eg,
irb(main):002:0> [1,2,3,4,5,6].map! {|x| x*2 }
=> [2, 4, 6, 8, 10, 12]
i'd like to double the element values except those loc on the 2nd and 5th
indices eg.
sorry if the answer is too obvious but i cannot find something like
array#each_with_index...
thanks in advance
kind regards -botp
At Fri, 8 Jul 2005 16:30:22 +0900,
Peña, Botp wrote in [ruby-talk:147536]:
> i'd like to double the element values except those loc on the 2nd and 5th
> indices eg.
$ ruby -renumerator -e 'p (1..6).enum_for(:each_with_index).map{|x,i|[2,5].include?(i) ? x : x*2}'
[2, 4, 3, 8, 10, 6]
--
Nobu Nakada
> [...] i cannot find something like array#each_with_index...
David Black was once President of Citizens for MWI, Inc.
(google "map with index" MWI).
Welcome!
You can add your own, of course.
module Enumerable
def map_with_index!
each_with_index do |e, ix|
self[ix] = yield e, ix
end
self
end
end
arr = (1..6).to_a
arr.map_with_index! do |e, ix|
[2, 5].include?(ix) ? e : e*2
end
p arr
#=> [2, 4, 3, 8, 10, 6]
daz
#> i'd like to double the element values except those loc on
#the 2nd and 5th
#> indices eg.
#
#$ ruby -renumerator -e 'p
#(1..6).enum_for(:each_with_index).map{|x,i|[2,5].include?(i) ?
#x : x*2}'
#[2, 4, 3, 8, 10, 6]
Hi Nobu,
Wow, cool. But my brain is very tiny compared to other rubyist brains.
i was hoping for something straight like,
>[1,2,3,4,5,6].map{|x,i|[2,5].include?(i) ? x : x*2}
=>[2, 4, 3, 8, 10, 6]
Maybe you can hack one for dumb rubyists like me.
thanks and kind regards -botp
#
#--
#Nobu Nakada
#
#> [...] i cannot find something like array#each_with_index...
#
#David Black was once President of Citizens for MWI, Inc.
#(google "map with index" MWI).
#
#Welcome!
#
#
#You can add your own, of course.
#
#module Enumerable
# def map_with_index!
# each_with_index do |e, ix|
# self[ix] = yield e, ix
# end
# self
# end
#end
#
#arr = (1..6).to_a
#arr.map_with_index! do |e, ix|
# [2, 5].include?(ix) ? e : e*2
#end
#
#p arr
##=> [2, 4, 3, 8, 10, 6]
uber cool and simple.
Thanks daz and DavidB.
kind regards -botp
#
#
#daz
#
#
#
#
Err, are you kidding? Enumerator isn't really that difficult - and you
don't need to modify Hash or other classes. It's a simple wrapper
delegating each to each_with_index. That simple.
Regards
robert
> I'd still like to see #man_with_index added to Enumerable.
"index" implies an ordering, which maps by definition don't have.
Shouldn't that be something along the lines of "#map_with_key"?
But point taken; it seems a natural thing, regardless of actual name,
to be in Enumerable.
Actually you're right about the ordering, and I think it should not be
in Enumerable. Hash#each_with_index has always struck me as pointless
for that reason: it just slaps essentially arbitrary integers on an
unordered collection. It also has always struck me as odd that
Enumerable has #each_with_index but lacks #index.
What I'd really like to see would be #each_with_index removed from
Enumerable, and pushed down to Array and other classes that need it
(like #index), and then #map_with_index added to Array too.
I think there's an RCR about this, probably, somewhere... possibly
rejected... :-) There's also been a lot of discussion here and
elsewhere about things like whether "enumerable" has to mean
"associated serially with integers", whether hashes should really be
understood as enumerable, whether hash keys are the equivalent of
array indices or whether, if hashes have a numerical "index", that
index is the equivalent of an array index, and if so, why hashes need
that extra layer of indexing, etc. (That's also come up in connection
with what an Array#to_hash method would do.)
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.
David
--
David A. Black
dbl...@wobblini.net
Key is ambiguous with Hashes... Apart from that: I'm not really sure
whether I like that idea. The basic property of an Enumerable is that it
can be enumerated with each. There are no guarantees about order etc.
each_with_index is really only there to simplify counting. If you want
map_with_index then you'd probably have to provide inject_with_index,
find_with_index etc. Personally I think that map_with_index is rarely
used to justify adding this to the std lib. Just my 0.02 EUR of course.
Btw, another variant for mapping with index:
enum.inject([]) {|ar,el| ar << ([0,3].include?(ar.size) ? el : el*2) }
Kind regards
robert
On Fri, 8 Jul 2005, Robert Klemme wrote:
> Michael Campbell wrote:
>> On 7/8/05, David A. Black <dbl...@wobblini.net> wrote:
>>> Hi --
>>
>>> I'd still like to see #man_with_index added to Enumerable.
>>
>> "index" implies an ordering, which maps by definition don't have.
>> Shouldn't that be something along the lines of "#map_with_key"?
>>
>> But point taken; it seems a natural thing, regardless of actual name,
>> to be in Enumerable.
>
> Key is ambiguous with Hashes... Apart from that: I'm not really sure
> whether I like that idea. The basic property of an Enumerable is that it
> can be enumerated with each. There are no guarantees about order etc.
> each_with_index is really only there to simplify counting. If you want
> map_with_index then you'd probably have to provide inject_with_index,
> find_with_index etc. Personally I think that map_with_index is rarely
> used to justify adding this to the std lib. Just my 0.02 EUR of course.
I'm not a stickler for symmetry (my slogan for Ruby is "The triumph of
balance over symmetry" :-) but it does seem a little arbitrary to have
it recognized that #each_with_index is important for arrays, and #map
is important, but #map_with_index isn't. (I emphasize "arrays"; I've
never seen a case where it was needed or useful for hashes.) I don't
think there's any implication that you'd have to have *_with_index.
Nor any demand: I've never seen anyone implement #find_with_index
(what exactly would it do? :-) but I've seen many of
Array#each_with_index.
In message "Re: accessing index inside map"
on Fri, 8 Jul 2005 19:20:48 +0900, "Robert Klemme" <bob....@gmx.net> writes:
|> #$ ruby -renumerator -e 'p
|> #(1..6).enum_for(:each_with_index).map{|x,i|[2,5].include?(i) ?
|> #
|> #[2, 4, 3, 8, 10, 6]
|>
|> Hi Nobu,
|>
|> Wow, cool. But my brain is very tiny compared to other rubyist brains.
|>
|> i was hoping for something straight like,
|>
|>> [1,2,3,4,5,6].map{|x,i|[2,5].include?(i) ? x : x*2}
|> =>[2, 4, 3, 8, 10, 6]
|>
|> Maybe you can hack one for dumb rubyists like me.
|
|Err, are you kidding? Enumerator isn't really that difficult - and you
|don't need to modify Hash or other classes. It's a simple wrapper
|delegating each to each_with_index. That simple.
We have vague plan to make enumerating method to return Enumerator
when no block is given in the future, so that
require 'enumerator'
(1..6).enum_for(:each_with_index).map{|x,i|[2,5].include?(i) ? x : x*2}
would be
(1..6).each_with_index.map{|x,i|[2,5].include?(i) ? x : x*2}
then. It's much simpler isn't it?
matz.
What about removing this completely from Enumerable and doing it like
this:
class Indexer
include Enumerable
def initialize(obj)
# @meth = meth
@obj = obj
end
def each
i = 0
@obj.each {|*a| yield *(a << i); i+=1}
self
end
end
module Enumerable
def indexer() Indexer.new(self) end
end
>> aa=[10,20,30]
=> [10, 20, 30]
>> aa.indexer.each {|a,i| printf "%4d. %s\n", i, a}
0. 10
1. 20
2. 30
=> #<Indexer:0x1017edc8 @obj=[10, 20, 30]>
>> aa.indexer.map {|a,i| a*i}
=> [0, 20, 60]
>> aa.indexer.inject([]) {|ar,(a,i)| ar << sprintf( "%4d. %s", i, a)}
=> [" 0. 10", " 1. 20", " 2. 30"]
>> hh={10=>:a, 20=>:b, 30=>:c}
=> {30=>:c, 20=>:b, 10=>:a}
>> hh.indexer.each {|(k,v),i| printf "%4d. %s => %s\n", i, k, v}
0. 30 => c
1. 20 => b
2. 10 => a
=> #<Indexer:0x101a1940 @obj={30=>:c, 20=>:b, 10=>:a}>
>> hh.indexer.map {|(k,v),i| "#{k}=>#{v}" * i}
=> ["", "20=>b", "10=>a10=>a"]
>> hh.indexer.inject([]) {|ar,((k,v),i)| ar << sprintf( "%4d. %s => %s",
i, k, v)}
=> [" 0. 30 => c", " 1. 20 => b", " 2. 10 => a"]
Admittedly the last example looks a bit awkward - but #inject is not easy
anyway and it stays consistend with the others.
Kind regards
robert
Count me as a member. I write this function approximately once for each
Ruby program I work on.
--
William <wmorgan-...@masanjin.net>
"Map" here means "collect", not "Hash".
--
William <wmorgan-...@masanjin.net>
Whoops, I may have read too much into your reply. Sorry.
Anyways, as Robert Klemme points out, the index is often useful in
counting, so I'd be favor of putting map_with_index in Enumerable.
--
William <wmorgan-...@masanjin.net>
Wow - I'd love to see that. Any potential drawbacks?
martin
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
> 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
>
>
How about:
irb(main):012:0> class Hash
irb(main):013:1> alias_method :orig_each, :each
irb(main):014:1> def each
irb(main):015:2> index = 0
irb(main):016:2> orig_each do |ek, ev|
irb(main):017:3* yield ek, ev, index
irb(main):018:3> index += 1
irb(main):019:3> end
irb(main):020:2> end
irb(main):021:1> end
=> nil
irb(main):022:0> h = {:a => 1, :b => 2, :c => 3}
=> {:c=>3, :a=>1, :b=>2}
irb(main):023:0> h.each {|ek, ev| p [ek , ev ]}
[:c, 3]
[:a, 1]
[:b, 2]
=> {:c=>3, :a=>1, :b=>2}
irb(main):026:0> h.each {|ek, ev, idx| p [ek, ev, idx]}
[:c, 3, 0]
[:a, 1, 1]
[:b, 2, 2]
=> {:c=>3, :a=>1, :b=>2}
This, as you already know, doesn't work with Arrays:
irb(main):027:0> def stuff
irb(main):028:1> yield 1,2
irb(main):029:1> end
=> nil
irb(main):030:0> stuff {|a| p a}
(irb):30: warning: multiple values for a block parameter (2 for 1)
from (irb):28
[1, 2]
=> nil
But that's being warned about, so I hope that, in the future, 'a' will
be set to 1, instead of [1,2]. (...which would make the above trick work
for Arrays.)
It wouldn't provide automatic "with index" functionality, as your hack
does, but it seems (to me) to be the "cleanest" change to allow you to
implement _with_index methods without polluting the methods list.
Sorry if this is a rehash (no pun intended) of stuff covered in the
ruby-core thread.
Devin
Fine, but see |*a| in Array examples below.
Where would the index go (?)
>
> This, as you already know, doesn't work with Arrays:
> irb(main):027:0> def stuff
> irb(main):028:1> yield 1,2
> irb(main):029:1> end
> => nil
> irb(main):030:0> stuff {|a| p a}
> (irb):30: warning: multiple values for a block parameter (2 for 1)
> from (irb):28
> [1, 2]
> => nil
>
> But that's being warned about, so I hope that, in the future, 'a' will
> be set to 1, instead of [1,2]. (...which would make the above trick work
> for Arrays.)
def stuff
yield [1,2] # yield one value, let assignment split them.
end
stuff {|a| p [a]} #-> [[1, 2]]
stuff {|a, b| p [a, b]} #-> [1, 2]
stuff {|a, b, c| p [a, b, c]} #-> [1, 2, nil]
stuff {|*a| p [a]} #-> [[[1, 2]]]
#-> |*a, index| would look ambiguous, here
>
> It wouldn't provide automatic "with index" functionality, as your hack
> does, but it seems (to me) to be the "cleanest" change to allow you to
> implement _with_index methods without polluting the methods list.
>
I'm sure it would have been desirable in the original #e_w_i
implementation, but difficult, therefore #e_w_i takes exactly
two arguments.
> Sorry if this is a rehash (no pun intended) of stuff covered in the
> ruby-core thread.
No danger ;-) That was a looong time ago.
>
> Devin
>
daz
>Fine, but see |*a| in Array examples below.
>Where would the index go (?)
>
If an assignment contains more lvalues than rvalues, the excess lvalues
are set to nil.
If an assignment contains more rvalues than lvalues, the excess rvalues
are ignores.
There are exceptions, however, for just one lvalue or just one rvalue.
a = 1,2 #=> [1, 2]
a #=> [1, 2]
a,b = [1,2] #=> [1, 2]
[a,b] #=> [1, 2]
I'm proposing we get rid of these heuristics. They can be explicitly be
enabled using asterisks.
*a = 1,2 #=> [1, 2]
a #=> [1, 2]
a,b = *[1,2] #=> [1, 2]
[a,b] #=> [1, 2]
So that the new result without *s would be:
a = 1,2 #=> [1, 2]
a #=> 1
a,b = [1,2] #=> [1, 2]
[a,b] #=> [[1, 2], nil]
This way, one could implement the C-equivalent of
class Array
alias_method :old_each, :each
def each
index = 0
old_each do |obj|
yield obj, index
index += 1
end
end
end
and then call:
["cat","dog","pin"].each {|obj| p obj}
["cat","dog","pin"].each {|obj,idx| puts "#{obj} at #{idx}"}
No doubt, this is strongly backwards-incompatible, but who am I to care? :P
Devin
..not a fan of Microsoft Clippy.
+1
Dan
Minor point, but you can also do
h.each_with_index { |(ek, ev), idx| p [ek, ev, idx] }
which solves C and D (though I guess introduces an "F").
--
William <wmorgan-...@masanjin.net>
A very strong point; one that I hadn't considered.
If the parens are left in, it's OK.
h.each_with_index { |(ek, ev), idx| p [ek, ev, idx] }
h.each { |(ek, ev) | p [ek, ev ] }
Dang, there's always a way with Ruby ;-)
daz
--
"Doctor, I'm cured." - A. Patient
#We have vague plan to make enumerating method to return Enumerator
#when no block is given in the future, so that
#
# require 'enumerator'
# (1..6).enum_for(:each_with_index).map{|x,i|[2,5].include?(i)
#? x : x*2}
#
#would be
#
# (1..6).each_with_index.map{|x,i|[2,5].include?(i) ? x : x*2}
#
#then. It's much simpler isn't it?
Hi sir Matz,
You're vague plan produces clearer ruby..
but isn't
each_with_index.map
so close to being
map_with_index
?
(1..6).map_with_index{|x,i|[2,5].include?(i) ? x : x*2} is very easy to my
tiny brain since all i have to remember is map (just like (in array) all i
have to focus is each, then i follow thru with each_with_index)
i'm sorry, i'm a map/collect fan so pardon my insistence :-)
thanks in advance.
kind regards
-botp -another spoiled nuby spoiled by map
#
# matz.
#
In message "Re: accessing index inside map"
on Mon, 11 Jul 2005 10:50:31 +0900, "Peña, Botp" <bo...@delmonte-phil.com> writes:
|# (1..6).each_with_index.map{|x,i|[2,5].include?(i) ? x : x*2}
|#
|#then. It's much simpler isn't it?
|but isn't
| each_with_index.map
|
|so close to being
| map_with_index
|
|?
Yes. But when we add map_with_index, we might be asked to add
collect_with_index, detect_with_index, inject_with_index, and all
other enumerable methods _with_index as well.
matz.
At Mon, 11 Jul 2005 10:50:31 +0900,
Peña, Botp wrote in [ruby-talk:147720]:
> You're vague plan produces clearer ruby..
>
> but isn't
> each_with_index.map
>
> so close to being
> map_with_index
>
> ?
>
> (1..6).map_with_index{|x,i|[2,5].include?(i) ? x : x*2} is very easy to my
> tiny brain since all i have to remember is map (just like (in array) all i
> have to focus is each, then i follow thru with each_with_index)
What about Enumerator#with_index?
[1,2,3,4,5,6].map.with_index {|x,i|[2,5].include?(i) ? x : x*2}
--
Nobu Nakada
I like that, personally. In fact each_with_index could become
each.with_index (though we obviously need the old one for backwards
compatibility.)
Ryan
At Mon, 11 Jul 2005 14:09:41 +0900,
Ryan Leavengood wrote in [ruby-talk:147729]:
Indeed.
$ ./ruby -renumerator -e 'p (1..6).to_enum.with_index{|x,i|p [x,i]}'
[1, 0]
[2, 1]
[3, 2]
[4, 3]
[5, 4]
[6, 5]
1..6
A patch based on the current implementation.
Index: ext/enumerator/enumerator.c
===================================================================
RCS file: /cvs/ruby/src/ruby/ext/enumerator/enumerator.c,v
retrieving revision 1.3.2.2
diff -U2 -p -r1.3.2.2 enumerator.c
--- ext/enumerator/enumerator.c 4 Nov 2004 01:20:50 -0000 1.3.2.2
+++ ext/enumerator/enumerator.c 11 Jul 2005 05:25:15 -0000
@@ -162,4 +162,27 @@ enumerator_each(obj)
}
+static VALUE
+enumerator_with_index_i(val, memo)
+ VALUE val, *memo;
+{
+ val = rb_yield_values(2, val, INT2FIX(*memo));
+ ++*memo;
+ return val;
+}
+
+static VALUE
+enumerator_with_index(obj)
+ VALUE obj;
+{
+ VALUE memo = 0;
+
+ obj = (VALUE)rb_node_newnode(NODE_MEMO,
+ rb_ivar_get(obj, id_enum_obj),
+ rb_to_id(rb_ivar_get(obj, id_enum_method)),
+ rb_ivar_get(obj, id_enum_args));
+ return rb_iterate((VALUE (*)_((VALUE)))enumerator_iter, obj,
+ enumerator_with_index_i, (VALUE)&memo);
+}
+
void
Init_enumerator()
@@ -183,4 +206,5 @@ Init_enumerator()
rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
+ rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0);
sym_each = ID2SYM(rb_intern("each"));
--
Nobu Nakada
In message "Re: accessing index inside map"
on Mon, 11 Jul 2005 14:29:24 +0900, nobuyoshi nakada <nobuyosh...@ge.com> writes:
| $ ./ruby -renumerator -e 'p (1..6).to_enum.with_index{|x,i|p [x,i]}'
| [1, 0]
| [2, 1]
| [3, 2]
| [4, 3]
| [5, 4]
| [6, 5]
| 1..6
|
|A patch based on the current implementation.
Can you commit this patch to the HEAD?
matz.
At Mon, 11 Jul 2005 14:57:00 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:147731]:
> | $ ./ruby -renumerator -e 'p (1..6).to_enum.with_index{|x,i|p [x,i]}'
> | [1, 0]
> | [2, 1]
> | [3, 2]
> | [4, 3]
> | [5, 4]
> | [6, 5]
> | 1..6
> |
> |A patch based on the current implementation.
>
> Can you commit this patch to the HEAD?
For performance, I'd like to make Enumerator a subclass of
Data instead of Object, in the HEAD.
Index: ext/enumerator/enumerator.c
===================================================================
RCS file: /cvs/ruby/src/ruby/ext/enumerator/enumerator.c,v
retrieving revision 1.5
diff -U2 -p -r1.5 enumerator.c
--- ext/enumerator/enumerator.c 2 Nov 2004 07:38:21 -0000 1.5
+++ ext/enumerator/enumerator.c 11 Jul 2005 07:38:27 -0000
@@ -18,5 +18,77 @@
static VALUE rb_cEnumerator;
static ID sym_each, sym_each_with_index, sym_each_slice, sym_each_cons;
-static ID id_new, id_enum_obj, id_enum_method, id_enum_args;
+#if !defined(HAVE_RB_PROC_CALL) || !defined(HAVE_RB_METHOD_CALL)
+static ID id_call;
+#endif
+
+static VALUE
+proc_call(proc, args)
+ VALUE proc, args;
+{
+#ifdef HAVE_RB_PROC_CALL
+ if (TYPE(args) != T_ARRAY) {
+ args = rb_values_new(1, args);
+ }
+ return rb_proc_call(proc, args);
+#else
+ return rb_funcall2(proc, id_call, 1, &args);
+#endif
+}
+
+static VALUE
+method_call(method, args)
+ VALUE method, args;
+{
+#ifdef HAVE_RB_METHOD_CALL
+ return rb_method_call(RARRAY(args)->len, RARRAY(args)->ptr, method);
+#else
+ return rb_funcall2(method, id_call, RARRAY(args)->len, RARRAY(args)->ptr);
+#endif
+}
+
+struct enumerator {
+ VALUE method;
+ VALUE proc;
+ VALUE args;
+ VALUE (*iter)_((VALUE, struct enumerator *));
+};
+
+static void enumerator_mark _((void *));
+static void
+enumerator_mark(p)
+ void *p;
+{
+ struct enumerator *ptr = p;
+ rb_gc_mark(ptr->method);
+ rb_gc_mark(ptr->proc);
+ rb_gc_mark(ptr->args);
+}
+
+static struct enumerator *
+enumerator_ptr(obj)
+ VALUE obj;
+{
+ struct enumerator *ptr;
+
+ Data_Get_Struct(obj, struct enumerator, ptr);
+ if (RDATA(obj)->dmark != enumerator_mark) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %s (expected Enumerable::Enumerator)",
+ rb_obj_classname(obj));
+ }
+ if (!ptr) {
+ rb_raise(rb_eArgError, "uninitialized enumerator");
+ }
+ return ptr;
+}
+
+static VALUE enumerator_iter_i _((VALUE, struct enumerator *));
+static VALUE
+enumerator_iter_i(i, e)
+ VALUE i;
+ struct enumerator *e;
+{
+ return rb_yield(proc_call(e->proc, i));
+}
static VALUE
@@ -26,5 +98,7 @@ obj_to_enum(obj, enum_args)
rb_ary_unshift(enum_args, obj);
- return rb_apply(rb_cEnumerator, id_new, enum_args);
+ return rb_class_new_instance(RARRAY(enum_args)->len,
+ RARRAY(enum_args)->ptr,
+ rb_cEnumerator);
}
@@ -33,5 +107,8 @@ enumerator_enum_with_index(obj)
VALUE obj;
{
- return rb_funcall(rb_cEnumerator, id_new, 2, obj, sym_each_with_index);
+ VALUE args[2];
+ args[0] = obj;
+ args[1] = sym_each_with_index;
+ return rb_class_new_instance(2, args, rb_cEnumerator);
}
@@ -39,8 +116,8 @@ static VALUE
each_slice_i(val, memo)
VALUE val;
- NODE *memo;
+ VALUE *memo;
{
- VALUE ary = memo->u1.value;
- long size = memo->u3.cnt;
+ VALUE ary = memo[0];
+ long size = (long)memo[1];
rb_ary_push(ary, val);
@@ -48,5 +125,5 @@ each_slice_i(val, memo)
if (RARRAY(ary)->len == size) {
rb_yield(ary);
- memo->u1.value = rb_ary_new2(size);
+ memo[0] = rb_ary_new2(size);
}
@@ -59,14 +136,14 @@ enum_each_slice(obj, n)
{
long size = NUM2LONG(n);
- NODE *memo;
- VALUE ary;
+ VALUE args[2], ary;
if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
- memo = rb_node_newnode(NODE_MEMO, rb_ary_new2(size), 0, size);
+ args[0] = rb_ary_new2(size);
+ args[1] = (VALUE)size;
- rb_iterate(rb_each, obj, each_slice_i, (VALUE)memo);
+ rb_iterate(rb_each, obj, each_slice_i, (VALUE)args);
- ary = memo->u1.value;
+ ary = args[0];
if (RARRAY(ary)->len > 0) rb_yield(ary);
@@ -78,5 +155,9 @@ enumerator_enum_slice(obj, n)
VALUE obj, n;
{
- return rb_funcall(rb_cEnumerator, id_new, 3, obj, sym_each_slice, n);
+ VALUE args[2];
+ args[0] = obj;
+ args[1] = sym_each_slice;
+ args[2] = n;
+ return rb_class_new_instance(3, args, rb_cEnumerator);
}
@@ -84,8 +165,8 @@ static VALUE
each_cons_i(val, memo)
VALUE val;
- NODE *memo;
+ VALUE *memo;
{
- VALUE ary = memo->u1.value;
- long size = memo->u3.cnt;
+ VALUE ary = memo[0];
+ long size = (long)memo[1];
if (RARRAY(ary)->len == size) {
@@ -104,10 +185,11 @@ enum_each_cons(obj, n)
{
long size = NUM2LONG(n);
- NODE *memo;
+ VALUE args[2];
if (size <= 0) rb_raise(rb_eArgError, "invalid size");
- memo = rb_node_newnode(NODE_MEMO, rb_ary_new2(size), 0, size);
+ args[0] = rb_ary_new2(size);
+ args[1] = (VALUE)size;
- rb_iterate(rb_each, obj, each_cons_i, (VALUE)memo);
+ rb_iterate(rb_each, obj, each_cons_i, (VALUE)args);
return Qnil;
@@ -118,5 +200,19 @@ enumerator_enum_cons(obj, n)
VALUE obj, n;
{
- return rb_funcall(rb_cEnumerator, id_new, 3, obj, sym_each_cons, n);
+ VALUE args[2];
+ args[0] = obj;
+ args[1] = sym_each_cons;
+ args[2] = n;
+ return rb_class_new_instance(3, args, rb_cEnumerator);
+}
+
+static VALUE enumerator_allocate _((VALUE));
+static VALUE
+enumerator_allocate(klass)
+ VALUE klass;
+{
+ struct enumerator *ptr;
+ return Data_Make_Struct(rb_cEnumerator, struct enumerator,
+ enumerator_mark, -1, ptr);
}
@@ -128,4 +224,5 @@ enumerator_initialize(argc, argv, obj)
{
VALUE enum_obj, enum_method, enum_args;
+ struct enumerator *ptr = enumerator_ptr(obj);
rb_scan_args(argc, argv, "11*", &enum_obj, &enum_method, &enum_args);
@@ -134,16 +231,25 @@ enumerator_initialize(argc, argv, obj)
enum_method = sym_each;
- rb_ivar_set(obj, id_enum_obj, enum_obj);
- rb_ivar_set(obj, id_enum_method, enum_method);
- rb_ivar_set(obj, id_enum_args, enum_args);
+ ptr->method = rb_obj_method(enum_obj, enum_method);
+ if (rb_block_given_p()) {
+ ptr->proc = rb_block_proc();
+ ptr->iter = enumerator_iter_i;
+ }
+ else {
+ ptr->iter = (VALUE (*) _((VALUE, struct enumerator *)))rb_yield;
+ }
+ ptr->args = enum_args;
- return Qnil;
+ return obj;
}
+static VALUE enumerator_iter _((VALUE));
static VALUE
enumerator_iter(memo)
- NODE *memo;
+ VALUE memo;
{
- return rb_apply(memo->u1.value, memo->u2.id, memo->u3.value);
+ struct enumerator *e = (struct enumerator *)memo;
+
+ return method_call(e->method, e->args);
}
@@ -152,14 +258,29 @@ enumerator_each(obj)
VALUE obj;
{
- VALUE val;
+ struct enumerator *e = enumerator_ptr(obj);
- obj = (VALUE)rb_node_newnode(NODE_MEMO,
- rb_ivar_get(obj, id_enum_obj),
- rb_to_id(rb_ivar_get(obj, id_enum_method)),
- rb_ivar_get(obj, id_enum_args));
- val = rb_iterate((VALUE (*)_((VALUE)))enumerator_iter, obj, rb_yield, 0);
+ return rb_iterate(enumerator_iter, (VALUE)e, e->iter, (VALUE)e);
+}
+
+static VALUE
+enumerator_with_index_i(val, memo)
+ VALUE val, *memo;
+{
+ val = rb_yield_values(2, val, INT2FIX(*memo));
+ ++*memo;
return val;
}
+static VALUE
+enumerator_with_index(obj)
+ VALUE obj;
+{
+ struct enumerator *e = enumerator_ptr(obj);
+ VALUE memo = 0;
+
+ return rb_iterate(enumerator_iter, (VALUE)e,
+ enumerator_with_index_i, (VALUE)&memo);
+}
+
void
Init_enumerator()
@@ -181,6 +302,8 @@ Init_enumerator()
rb_include_module(rb_cEnumerator, rb_mEnumerable);
+ rb_define_alloc_func(rb_cEnumerator, enumerator_allocate);
rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
+ rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0);
sym_each = ID2SYM(rb_intern("each"));
@@ -189,7 +312,6 @@ Init_enumerator()
sym_each_cons = ID2SYM(rb_intern("each_cons"));
- id_new = rb_intern("new");
- id_enum_obj = rb_intern("enum_obj");
- id_enum_method = rb_intern("enum_method");
- id_enum_args = rb_intern("enum_args");
+#if !defined(HAVE_RB_PROC_CALL) || !defined(HAVE_RB_METHOD_CALL)
+ id_call = rb_intern("call");
+#endif
}
Index: ext/enumerator/extconf.rb
===================================================================
RCS file: /cvs/ruby/src/ruby/ext/enumerator/extconf.rb,v
retrieving revision 1.1
diff -U2 -p -r1.1 extconf.rb
--- ext/enumerator/extconf.rb 1 Nov 2004 05:04:04 -0000 1.1
+++ ext/enumerator/extconf.rb 23 Jun 2005 02:55:54 -0000
@@ -1,2 +1,5 @@
require 'mkmf'
+
+%w"rb_obj_method rb_method_call".all? {|f| have_func(f, "ruby.h")}
+have_func("rb_proc_call", "ruby.h")
create_makefile('enumerator')
--
Nobu Nakada
In message "Re: accessing index inside map"
on Mon, 11 Jul 2005 16:41:45 +0900, nobuyoshi nakada <nobuyosh...@ge.com> writes:
|> Can you commit this patch to the HEAD?
|
|For performance, I'd like to make Enumerator a subclass of
|Data instead of Object, in the HEAD.
It's all up to you. Although I feel no need to check rb_proc_call,
since enumerator is bundled with Ruby itself.
matz.
At Mon, 11 Jul 2005 16:48:29 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:147737]:
> |> Can you commit this patch to the HEAD?
> |
> |For performance, I'd like to make Enumerator a subclass of
> |Data instead of Object, in the HEAD.
>
> It's all up to you. Although I feel no need to check rb_proc_call,
> since enumerator is bundled with Ruby itself.
Static function proc_call() is defined now but rb_proc_call()
is not. Also rb_obj_method() and rb_method_call().
--
Nobu Nakada
In message "Re: accessing index inside map"
on Mon, 11 Jul 2005 17:37:07 +0900, nobuyoshi nakada <nobuyosh...@ge.com> writes:
|> It's all up to you. Although I feel no need to check rb_proc_call,
|> since enumerator is bundled with Ruby itself.
|
|Static function proc_call() is defined now but rb_proc_call()
|is not. Also rb_obj_method() and rb_method_call().
I know. But if you need those functions, just export (with proper
prefix). I see no need for have_func() check in extconf.rb.
matz.
#> You're vague plan produces clearer ruby..
#>
#> but isn't
#> each_with_index.map
#>
#> so close to being
#> map_with_index
#>
#> ?
#>
#> (1..6).map_with_index{|x,i|[2,5].include?(i) ? x : x*2} is
#very easy to my
#> tiny brain since all i have to remember is map (just like
#(in array) all i
#> have to focus is each, then i follow thru with each_with_index)
#
#What about Enumerator#with_index?
#
# [1,2,3,4,5,6].map.with_index {|x,i|[2,5].include?(i) ? x : x*2}
imho, #with_index pleads for a better name. It sounds far from being a
method; which leads me to incline back to your original proposition of
each_with_index.map...
another concern also is that sir matz already warned me of the spilling of
the *_with_index naming all over the place..
OK, i'll be shooting for the moon here. How about passing a parameter? Is it
then possible to make #each or #map same behavior w #each_with_index or
#map_with_index or whatever *_with_index so that the ff works,
def each(with_index=true) do... end
def map(with_index=true) do ... end
and thus, we can do
[1,2,3,4,5,6].map(true) {|x,i|[2,5].include?(i) ? x : x*2}
I think this one does not break old code, no?
I find index being more of an attribute than a pure method.
And you are all right, I just need the index, why do i need another set of
methods?
Again, imho.
Thanks and kind regards -botp
#
#--
#Nobu Nakada
#
#> OK, i'll be shooting for the moon here. How about passing a parameter? Is
it
#> then possible to make #each or #map same behavior w #each_with_index or
#> #map_with_index or whatever *_with_index so that the ff works,
#>
#> def each(with_index=true) do... end
#>
#> def map(with_index=true) do ... end
#>
#> and thus, we can do
#>
#> [1,2,3,4,5,6].map(true) {|x,i|[2,5].include?(i) ? x : x*2}
#
#I'd hope not to see that. I'm actually hoping that maybe someday all
#the boolean flag methods (like instance_methods(true) etc.) will be
#changed :-) They've always seemed to me to be the most cryptic thing
#in all of Ruby.
#
Yes, you are correct. But there is some sort of give and take here or
balance if i may say (i think i heard the word balance fr you, sir David)..
How about
[1,2,3,4,5,6].map(WITH_INDEX) {|x,i|[2,5].include?(i) ? x : x*2}
or
[1,2,3,4,5,6].map(with_index=>true) {|x,i|[2,5].include?(i) ? x : x*2}
if we don't pass flags or attribs, wouldn't that make creating additional
methods like
each
each_with_index
map
map_with_index
collect
collect_with_index
..
?
it's with_index es all over..
Also, if we create a with_index method, wouldn't that be invoking another
method just for the index, like
each_with_index.map
or
map.with_index
Wouldn't my algorithm slow down? Does not the chain begs for a single method
-which brings us back to the original question...?
kind regards -botp
#David
To second that: it's generally considered not very clean to have a flag
change a method's behavior. I think this is a case where another method
is superior to having one method behavior switched. Even from an
implementation perspective: either you end up with a single method that
consists of if-then-else on the top level with one implementation in each
branch or you end up with a lot of if-then-else inside the looping. The
latter is inefficient and the former is more efficient but then you can
have to methods anyway.
Kind regards
robert
> How about
>
> [1,2,3,4,5,6].map(WITH_INDEX) {|x,i|[2,5].include?(i) ? x : x*2}
> or
> [1,2,3,4,5,6].map(with_index=>true) {|x,i|[2,5].include?(i) ? x : x*2}
How about a more 'rails' like implementation:
[1,2,3].map(:with_index) { |x,ix| ... }
[1,2,3].collect(:with_index) { |x,ix| ... }
[1,2,3].collect!(:with_index) { |x,ix| ... }
[1,2,3].each(:with_index) { |x,ix| ... }
[1,2,3].each { |x| ... }
--
Jim Freeze
It is. None are required; not even each_with_index.
>
> Also, if we create a with_index method, wouldn't that be invoking another
> method just for the index, like
>
> each_with_index.map
> or
> map.with_index
>
> Wouldn't my algorithm slow down? Does not the chain begs for a single method
> -which brings us back to the original question...?
>
Yes. I'm taking sedatives while it's seriously being considered that
an Array should become an Enumerator in order to use its #with_index method
on an intermediate #map object to produce ... an Array ... instead of the
regular #map method with an optional index.
I would have posted a link to the ruby-core thread, but it was my
first post to a mailing list and my MAIL settings were different
from my NEWS settings, so it's messed up (quoted-printable MIME).
Here's a text version (minor EDITs). I'm sure it'll be relevant to you.
http://www.d10.karoo.net/ruby/xv/ruby-core-1495.txt
You'll see examples using bang! methods.
(( I'd be fascinated to see map!.with_index or map.with_index!
produce anything intelligible using require 'enumerator'. ))
>
> kind regards -botp
>
daz
> How about a more 'rails' like implementation:
>
> [1,2,3].map(:with_index) { |x,ix| ... }
> [1,2,3].collect(:with_index) { |x,ix| ... }
> [1,2,3].collect!(:with_index) { |x,ix| ... }
> [1,2,3].each(:with_index) { |x,ix| ... }
> [1,2,3].each { |x| ... }
Ugh. My impression has always been that they couldn't make up their
minds, or, didn't think hard enough about picking good names the
first time around.
--
Eric Hodel - drb...@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04
#> Wouldn't my algorithm slow down? Does not the chain begs for
#a single method
#> -which brings us back to the original question...?
#>
#
#Yes. I'm taking sedatives while it's seriously being considered that
#an Array should become an Enumerator in order to use its
##with_index method
#on an intermediate #map object to produce ... an Array ...
#instead of the
#regular #map method with an optional index.
#
#I would have posted a link to the ruby-core thread, but it was my
#first post to a mailing list and my MAIL settings were different
#from my NEWS settings, so it's messed up (quoted-printable MIME).
#
#Here's a text version (minor EDITs). I'm sure it'll be
#relevant to you.
#
# http://www.d10.karoo.net/ruby/xv/ruby-core-1495.txt
#
#You'll see examples using bang! methods.
#
#(( I'd be fascinated to see map!.with_index or map.with_index!
# produce anything intelligible using require 'enumerator'. ))
#
cool. you're a ruby hacker indeed.
but imho (my opinion only) i do not like the ||.x notation.
how about
# we only need the index
[1,2,3].each() {|i|* printf i,"\n"}
# we need the val and the index
[1,2,3].each() {|x,i|* printf x,i,"\n"}
# in other words, we always get the last param as the index
[[0,1],[1,2],[3,4]].each() {|x,y,i|* printf x,i,"\n"}
ie, we put a star after the param delimiters || to signal an index. It's
like telling the programmer/reader "*hint-hint*"
kind regards -botp
#daz
#
#
#
#
OK. You're not the first and you won't be the last ;-)
But more important than how it looks is whether it alters meaning.
>
> how about
>
> # we only need the index
> [1,2,3].each() {|i|* printf i,"\n"}
>
> # we need the val and the index
> [1,2,3].each() {|x,i|* printf x,i,"\n"}
>
> # in other words, we always get the last param as the index
> [[0,1],[1,2],[3,4]].each() {|x,y,i|* printf x,i,"\n"}
>
> ie, we put a star after the param delimiters || to signal an index. It's
> like telling the programmer/reader "*hint-hint*"
>
[1].each { |n, i| x=n; p x } #-> 1
[1].each { |n, i|* x=n; p x } #-> [1]
Great care is needed when adding beyond the |bars| into code territory.
But /no damage/ is caused by thinking aloud :-)
For #each_with_index, the index is passed as a block parameter.
That method has _two_ block parameters. That's probably why we
expect to see the index there in an alternative implementation
with the same result.
[2].each { |n | p [n ] } #-> [2]
[2].each_with_index { |n, index | p [n, index] } #-> [2, 0]
Let's say that we were offered no choice of name and that an
automatic block index were called $INDX.
[2].meth { |n | p [n ] } #-> [2]
[2].meth { |n | p [n, $INDX] } #-> [2, 0]
#meth passes _one_ argument and, regardless of whether or not
we use the $INDX, it still only passes _one_ argument.
'$INDX' isn't passed at all ... it's just ... /there/.
IMPO, auto-index-naming wouldn't belong between the bars but I
would be interested in hearing contrasting views.
Your suggestion, correctly, highlights the problem that an extra
block parameter, by itself, doesn't indicate to the parser that
it's an index. Your extra '*' marker does that.
Interesting.
daz
#> how about
#>
#> # we only need the index
#> [1,2,3].each() {|i|* printf i,"\n"}
#>
#> # we need the val and the index
#> [1,2,3].each() {|x,i|* printf x,i,"\n"}
#>
#> # in other words, we always get the last param as the index
#> [[0,1],[1,2],[3,4]].each() {|x,y,i|* printf x,i,"\n"}
#>
#> ie, we put a star after the param delimiters || to signal an
#index. It's
#> like telling the programmer/reader "*hint-hint*"
#>
#
#[1].each { |n, i| x=n; p x } #-> 1
#[1].each { |n, i|* x=n; p x } #-> [1]
ouch! am i hopeless here ? :-(
how about we give more affinity to |n,i|* than to *x ? The spacing clearly
shows, right?
#
#Great care is needed when adding beyond the |bars| into code territory.
#But /no damage/ is caused by thinking aloud :-)
yes, thanks.
#
#For #each_with_index, the index is passed as a block parameter.
#That method has _two_ block parameters. That's probably why we
#expect to see the index there in an alternative implementation
#with the same result.
#
#[2].each { |n | p [n ] } #-> [2]
#[2].each_with_index { |n, index | p [n, index] } #-> [2, 0]
#
#Let's say that we were offered no choice of name and that an
#automatic block index were called $INDX.
#
#[2].meth { |n | p [n ] } #-> [2]
#[2].meth { |n | p [n, $INDX] } #-> [2, 0]
#
##meth passes _one_ argument and, regardless of whether or not
#we use the $INDX, it still only passes _one_ argument.
#'$INDX' isn't passed at all ... it's just ... /there/.
#
correct. a $ variable would be great also.
I'm not sure though how it will behave in nested loops or blocks though..
#IMPO, auto-index-naming wouldn't belong between the bars but I
#would be interested in hearing contrasting views.
imho, i treat blocks as just that, blocks. I do not want it to carry any
baggage coming fr outside, including the index. But that is just me.
#
#Your suggestion, correctly, highlights the problem that an extra
#block parameter, by itself, doesn't indicate to the parser that
#it's an index. Your extra '*' marker does that.
I couldn't think of any simple yet visible marker, so the star "*".
kind regards -botp
#
#Interesting.
#
#
At Fri, 8 Jul 2005 23:02:11 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:147565]:
> We have vague plan to make enumerating method to return Enumerator
> when no block is given in the future, so that
>
> require 'enumerator'
> (1..6).enum_for(:each_with_index).map{|x,i|[2,5].include?(i) ? x : x*2}
>
> would be
>
> (1..6).each_with_index.map{|x,i|[2,5].include?(i) ? x : x*2}
>
> then. It's much simpler isn't it?
Is this plan going on?
I noticed that #map and #collect return an Array if no block
is given, instead of raising an exception.
Index: enum.c
===================================================================
RCS file: /cvs/ruby/src/ruby/enum.c,v
retrieving revision 1.58
diff -U2 -p -r1.58 enum.c
--- enum.c 30 Jun 2005 16:45:33 -0000 1.58
+++ enum.c 12 Jul 2005 03:54:56 -0000
@@ -18,4 +18,63 @@ VALUE rb_mEnumerable;
static ID id_each, id_eqq, id_cmp;
+#ifndef BUILTIN_ENUMERATOR
+#define BUILTIN_ENUMERATOR 1
+#endif
+
+#if BUILTIN_ENUMERATOR
+#define Init_enumerator static init_enumerator
+#include "ext/enumerator/enumerator.c"
+#undef Init_enumerator
+#else
+static ID id_enumerator;
+#endif
+
+void
+Init_enumerator()
+{
+#if BUILTIN_ENUMERATOR
+ char *src = ruby_sourcefile = rb_source_filename("enumerator");
+ init_enumerator();
+ rb_provide(src);
+#else
+ id_enumerator = rb_intern("Enumerator");
+ rb_autoload(rb_mEnumerable, id_enumerator, "enumerator");
+#endif
+}
+
+static VALUE
+enumeratorize(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE enum_method = ID2SYM(rb_frame_this_func());
+#if BUILTIN_ENUMERATOR
+ VALUE enum_obj = enumerator_allocate(rb_cEnumerator);
+ struct enumerator *ptr = enumerator_ptr(enum_obj);
+
+ ptr->method = rb_obj_method(obj, enum_method);
+ if (rb_block_given_p()) {
+ ptr->proc = rb_block_proc();
+ ptr->iter = enumerator_iter_i;
+ }
+ else {
+ ptr->iter = (VALUE (*) _((VALUE, struct enumerator *)))rb_yield;
+ }
+ if (argc) ptr->args = rb_ary_new4(argc, argv);
+
+ return enum_obj;
+#else
+ VALUE enumerator = rb_const_get(rb_mEnumerable, id_enumerator);
+ VALUE args = rb_ary_new2(argc + 2);
+
+ RARRAY(args)->ptr[0] = obj;
+ RARRAY(args)->ptr[1] = enum_method;
+ MEMCPY(RARRAY(args)->ptr + 2, argv, VALUE, argc);
+ RARRAY(args)->len = argc + 2;
+ return rb_class_new_instance(RARRAY(args)->len, RARRAY(args)->ptr, enumerator);
+#endif
+}
+
VALUE
rb_each(obj)
@@ -115,4 +174,6 @@ enum_find(argc, argv, obj)
rb_scan_args(argc, argv, "01", &if_none);
+ if (!rb_block_given_p())
+ return enumeratorize(argc, argv, obj);
rb_iterate(rb_each, obj, find_i, (VALUE)&memo);
if (memo != Qundef) {
@@ -152,6 +213,9 @@ enum_find_all(obj)
VALUE obj;
{
- VALUE ary = rb_ary_new();
+ VALUE ary;
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
+ ary = rb_ary_new();
rb_iterate(rb_each, obj, find_all_i, ary);
@@ -184,6 +248,9 @@ enum_reject(obj)
VALUE obj;
{
- VALUE ary = rb_ary_new();
+ VALUE ary;
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
+ ary = rb_ary_new();
rb_iterate(rb_each, obj, reject_i, ary);
@@ -226,7 +293,10 @@ enum_collect(obj)
VALUE obj;
{
- VALUE ary = rb_ary_new();
+ VALUE ary;
+
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
- rb_iterate(rb_each, obj, rb_block_given_p() ? collect_i : collect_all, ary);
+ ary = rb_ary_new();
+ rb_iterate(rb_each, obj, collect_i, ary);
return ary;
@@ -344,4 +414,6 @@ enum_partition(obj)
VALUE ary[2];
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
ary[0] = rb_ary_new();
ary[1] = rb_ary_new();
@@ -477,4 +549,6 @@ enum_sort_by(obj)
long i;
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
if (TYPE(obj) == T_ARRAY) {
ary = rb_ary_new2(RARRAY(obj)->len);
@@ -764,5 +838,6 @@ enum_min_by(obj)
VALUE memo[2];
- rb_need_block();
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
memo[0] = Qundef;
memo[1] = Qnil;
@@ -807,5 +882,6 @@ enum_max_by(obj)
VALUE memo[2];
- rb_need_block();
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
memo[0] = Qundef;
memo[1] = Qnil;
@@ -845,4 +921,6 @@ enum_member(obj, val)
VALUE memo[2];
+ if (!rb_block_given_p()) return enumeratorize(1, &val, obj);
+
memo[0] = val;
memo[1] = Qfalse;
@@ -882,5 +960,6 @@ enum_each_with_index(obj)
VALUE memo = 0;
- rb_need_block();
+ if (!rb_block_given_p()) return enumeratorize(0, 0, obj);
+
rb_iterate(rb_each, obj, each_with_index_i, (VALUE)&memo);
return obj;
Index: inits.c
===================================================================
RCS file: /cvs/ruby/src/ruby/inits.c,v
retrieving revision 1.9
diff -U2 -p -r1.9 inits.c
--- inits.c 19 Dec 2003 03:58:57 -0000 1.9
+++ inits.c 12 Jul 2005 03:49:43 -0000
@@ -19,4 +19,5 @@ void Init_Comparable _((void));
void Init_Dir _((void));
void Init_Enumerable _((void));
+void Init_enumerator _((void));
void Init_Exception _((void));
void Init_syserr _((void));
@@ -81,4 +82,5 @@ rb_call_inits()
Init_GC();
Init_marshal();
+ Init_enumerator();
Init_version();
}
--
Nobu Nakada
In message "Re: accessing index inside map"
on Tue, 12 Jul 2005 12:56:50 +0900, nobuyoshi nakada <nobuyosh...@ge.com> writes:
|> (1..6).each_with_index.map{|x,i|[2,5].include?(i) ? x : x*2}
|>
|> then. It's much simpler isn't it?
|
|Is this plan going on?
|
|I noticed that #map and #collect return an Array if no block
|is given, instead of raising an exception.
I agree with the plan, so that someone come up with the implementation
before I do, I'd glad to merge it. ... boy, you did that.
I think it's a good chance. Can you merge enumerator in the following
steps?
* merge ext/enumerator/enumerator.c to enum.c
* remove ext/enumerator directory.
* patch enum.c as you did.
matz.
At Tue, 12 Jul 2005 13:08:33 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:147835]:
> I think it's a good chance. Can you merge enumerator in the following
> steps?
>
> * merge ext/enumerator/enumerator.c to enum.c
> * remove ext/enumerator directory.
> * patch enum.c as you did.
Enumerator needs its own initialization routine to be called
after Init_GC() got run, so one alternative Would be to move
enumerator.c to the top directory.
Of course, they are still small enough to merge.
Which is better?
--
Nobu Nakada
In message "Re: accessing index inside map"
on Tue, 12 Jul 2005 15:17:53 +0900, nobuyoshi nakada <nobuyosh...@ge.com> writes:
|Enumerator needs its own initialization routine to be called
|after Init_GC() got run, so one alternative Would be to move
|enumerator.c to the top directory.
Adding Init_Enumerable() as well in the enum.c seems enough for me.
Don't forget to modify inits.c too.
matz.
If we're making this general, I till think 'counter' would be a far better
name than 'index'
martin
I'm joining this converstation late, but ISTM that the fundamental issue
here is that one object may have multiple enumeration methods (e.g. #each
and #each_with_index), but the methods in Enumerable are only able to call
#each. So perhaps a solution is to be able to choose the name of the
enumerator method to call.
e.g.
module Enumerable
def altmap(meth=:each)
res = []
send(meth) { |*args| res << yield(*args) }
res
end
end
a = [10,20,30,40]
p a.altmap { |x| x*2 } #=> [20, 40, 60, 80]
p a.altmap(:each_with_index) { |x,i| x+i } #=> [10, 21, 32, 43]
h = {1=>"one", 2=>"two", 3=>"three"}
p h.altmap(:each_key) { |x| x + x } #=> [2, 4, 6]
p h.altmap(:each_value) { |x| x + x } #=> ["oneone", "twotwo", "threethree"]
Then, Enumerable doesn't have to care about the concept of what's an "index"
- conceptually we move #each_with_index into the object being enumerated,
and it can provide whatever index makes sense to it. (Enumerable could still
provide a default #each_with_index as it does now, though)
Aside: I always found each_with_index to be odd, because we have
myhash.each { |key,value| ... }
myarray.each_with_index { |value,key| ... }
I'd like to be able to think of an Array as a special case of Hash, where
the keys just happen to be Integers starting from 0.
This could be regularised if Hash#each_with_index did { |k,v| yield v,k }
Regards,
Brian.
P.S. I note that some Enumerable methods already take arguments:
detect/find, grep, inject could have meth=:each as a later argument for
backwards-compatibility.
The only awkward ones are to_set and zip. Even then, they could be made
backwards-compatible by checking if the first argument is a Symbol and using
it as a method name in that case.
I don't really see the usefulness of the foo.to_set(klass,*args) syntax
anyway, given that it just calls klass.new(foo,*args). However, I can see
uses for
h.to_set(:each_key)
h.to_set(:each_value)
Regards,
Brian.
IMHO the enumerator approach is slightly superior as your suggestion can
lead to conflicts with current method arguments as you have noticed.
Personally I don't have a problem using
a.to_enum(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
I think this is more flexible and modularized. And the overhead of a new
instance creation is usually neglectible compared to the iteration
overhead.
Kind regards
robert
"Do the simplest thing which will possibly work" :-)
Actually I think my version reads better:
a.inject(0,:each_with_index) {|sum,(a,i)| sum + a*i}
"Starting with 0, call :each_with_index with the following block".
Regards,
Brian.
i=-1;[1,2,3,4,5,6].map{|x|[2,5].include?(i+=1) ? x : x*2}
#=> [2, 4, 3, 8, 10, 6]
You can, of course, use this idiom to emulate *_with_index
functionality for (almost?) any iterator method.
daz
--
Never seek simplicity where complexity will suffice.
- (paraphrase ;-)
Well, apparently we have differing opinions on this. Personally I find
the Enumerator approach simpler as it does not clutter the original's
class interface (introducing an optional parameter on each method doubles
the number of legal invocations). :-)
> Actually I think my version reads better:
>
> a.inject(0,:each_with_index) {|sum,(a,i)| sum + a*i}
>
> "Starting with 0, call :each_with_index with the following block".
There is a chance that folks who don't know #inject might read "start with
index 0".
Well, I think a vote is in order. Did you consider submitting this as
RCR?
Kind regards
robert
At Tue, 12 Jul 2005 15:47:35 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:147846]:
> |Enumerator needs its own initialization routine to be called
> |after Init_GC() got run, so one alternative Would be to move
> |enumerator.c to the top directory.
>
> Adding Init_Enumerable() as well in the enum.c seems enough for me.
> Don't forget to modify inits.c too.
I think "enumerator" feature is needed to be provided for
backward compatibility's sake. And rb_provide() can't be
called before Init_load() (not Init_GC(), sorry).
# Now I'm merging documents in enumerator.txt to enumerator.c.
--
Nobu Nakada
My own interpretation of "do the simplest" includes "don't introduce any
unnecessary abstractions". Arguably, the 'optional' parameter is always
there; it just happens to default to :each. There are lots of similar cases
in the Ruby standard library.
The Enumerator stuff is new to me, since I'm still just using 1.8.2.
However, I find obj.to_enum(:each_with_index) confusing; after all, obj is
*already* enumerable. Making this more general, you might get
obj.rename(:each=>:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
Creating an object just to say "call #each_with_index instead of #each"
seems wasteful unless you plan to re-use it, given that you could just call
#each_with_index in the first place.
However, perhaps 'to_enum' could be called something friendlier, e.g.
obj.using(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
In fact, even
obj.enum(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
reads better to me, as it's not stressing the creation of an intermediate
object. to_foo looks like you are converting obj into something completely
different, rather than just adding a temporary wrapper.
From the OP's question, then, it's a question of
[1,2,3,4,5,6].map(:each_with_index) { |x,i| [2,5].include?(i) ? x : x*2 }
versus
[1,2,3,4,5,6].enum(:each_with_index).map { |x,i| [2,5].include?(i) ? x : x*2 }
I still prefer the first :-) It's a mapping operation, applied directly to
an Array.
> Well, I think a vote is in order. Did you consider submitting this as
> RCR?
I'm not familiar with the RCR process.
I just thought I'd wait and see what the list members (and Matz in
particular) thought. This idea seems sufficiently obvious that I guess
there's some reason why it has not been implemented already.
Regards,
Brian.
On Wed, 13 Jul 2005, Brian Candler wrote:
>> Well, apparently we have differing opinions on this. Personally I find
>> the Enumerator approach simpler as it does not clutter the original's
>> class interface (introducing an optional parameter on each method doubles
>> the number of legal invocations). :-)
>
> My own interpretation of "do the simplest" includes "don't introduce any
> unnecessary abstractions". Arguably, the 'optional' parameter is always
> there; it just happens to default to :each. There are lots of similar cases
> in the Ruby standard library.
>
> The Enumerator stuff is new to me, since I'm still just using 1.8.2.
> However, I find obj.to_enum(:each_with_index) confusing; after all, obj is
> *already* enumerable. Making this more general, you might get
>
> obj.rename(:each=>:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
>
> Creating an object just to say "call #each_with_index instead of #each"
> seems wasteful unless you plan to re-use it, given that you could just call
> #each_with_index in the first place.
>
> However, perhaps 'to_enum' could be called something friendlier, e.g.
>
> obj.using(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
>
> In fact, even
>
> obj.enum(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
>
> reads better to me, as it's not stressing the creation of an intermediate
> object. to_foo looks like you are converting obj into something completely
> different, rather than just adding a temporary wrapper.
I wonder whether one might (re)introduce the "iter[ator]" set of
terms, rather than enumerator. I'm not totally clear on the relation
between them, so I may be off the mark, but I was thinking of things
like:
obj.iterate_with(:each_with_index).inject ...
a little bit on the model of sort_by. Then again, this may be getting
dangerously close to the rejected RCR at
http://www.rcrchive.net/rejected.html#rcr50.
I have to admit I haven't totally assimilated the Enumerator
mechanisms, which strike me as very useful and powerful but sort of a
secondary system overlaid on the whole iterator and/or enumerable
thing. I guess the barrier, for me, is having the separate Enumerator
object, rather than just having it happen in the course of calling a
method. I guess that's also what's powerful about it :-)
> I'm not familiar with the RCR process.
David
--
David A. Black
dbl...@wobblini.net
At Wed, 13 Jul 2005 01:10:19 +0900,
Brian Candler wrote in [ruby-talk:147876]:
> However, perhaps 'to_enum' could be called something friendlier, e.g.
>
> obj.using(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
`using' sounds too general.
> In fact, even
>
> obj.enum(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
>
> reads better to me, as it's not stressing the creation of an intermediate
> object. to_foo looks like you are converting obj into something completely
> different, rather than just adding a temporary wrapper.
Enumerable#to_enum has an alias named as #enum_for.
--
Nobu Nakada
How about just `for'? That word is already strongly associated
with enumeration, and connects well to the `each_*'.
obj.for(:each_with_index).inject(0) { |sum, (a, i)| sum + a * i }
However, ``for each inject'' sounds pretty weird, since we're not
injecting something into each element --- we're injecting it into the
whole collection.
I don't know, just a thought.
--
Daniel Brockman <dan...@brockman.se>
It's in 1.8.2 and I think it's been around for some time, maybe even as
far back as 1.7.*.
> However, I find obj.to_enum(:each_with_index) confusing; after all,
> obj is *already* enumerable.
Well, to_enum is rather a shorthand for to_enumerator. There's also
enum_for (as Nobu pointed out).
> Making this more general, you might get
>
> obj.rename(:each=>:each_with_index).inject(0) {|sum,(a,i)| sum +
> a*i}
With this naming I'd rather associate that :each is renamed on the
instance obj to :each_with_index and that this change is permanent. The
to_xyz notation clearly indicates a conversion i.e. you expect to get a
new isntance.
> Creating an object just to say "call #each_with_index instead of
> #each"
> seems wasteful unless you plan to re-use it, given that you could
> just call #each_with_index in the first place.
It's not that uncommon in OO oriented languages to create temporary
objects. For example command pattern has often this property: you create
an instance, set all properties you need for processing, execute it, fetch
the result and drop the instance again. This is a nice way of doing
housekeeping for temporary state that is solely associated with the
calculation.
The overhead in this case is neglectible in the general case, i.e., an
iteration that creates lots of new objects along the way.
> However, perhaps 'to_enum' could be called something friendlier, e.g.
>
> obj.using(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
using for what?
> In fact, even
>
> obj.enum(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
That reads better IMHO.
> reads better to me, as it's not stressing the creation of an
> intermediate object. to_foo looks like you are converting obj into
> something completely different, rather than just adding a temporary
> wrapper.
Yeah, maybe. Anyway, this is far better than rename IMHO.
> From the OP's question, then, it's a question of
>
> [1,2,3,4,5,6].map(:each_with_index) { |x,i| [2,5].include?(i) ? x :
> x*2 }
>
> versus
>
> [1,2,3,4,5,6].enum(:each_with_index).map { |x,i| [2,5].include?(i)
> ? x : x*2 }
>
> I still prefer the first :-) It's a mapping operation, applied
> directly to
> an Array.
>
>> Well, I think a vote is in order. Did you consider submitting this
>> as RCR?
>
> I'm not familiar with the RCR process.
It's fairly easy: you register, create your RCR (there are some helpful
questions on the website) and submit it. Then you wait for comments.
DAB> See http://www.rcrchive.net.
> I just thought I'd wait and see what the list members (and Matz in
> particular) thought. This idea seems sufficiently obvious that I guess
> there's some reason why it has not been implemented already.
Certainly also a good approach. The main advantage I see with RCR's is
that there is a reference point and all the comments are tied together.
Kind regards
robert
The preposition "for" doesn't really sound right here. I don't think we are
enumerating "for" each_with_index; we are enumerating "with"
each_with_index, or "using", or perhaps "over".
The underscore also seems to break up the line visually. Now I know the full
details, I can understand the following, but there are two visual breaks
between the 'map' and the object being enumerated over:
obj.enum_for(:each_with_index).map { .. }
^ ^
break break
I think that
obj.enum(:each_with_index).map { .. }
is more clearly a single method call separating the object and its map.
The idea of an enumeration method returning an Enumerator if called without
a block is interesting:
obj.each_with_index.map { .. }
although this means that every enumeration method you write needs extra code
to support this behaviour. And I dread to think what happens if you start to
chain these methods :-)
This mechanism requires every programmer to do more work in their
enumeration methods. In that case, I still prefer to modify every method in
Enumerable instead: i.e.
obj.map(:each_with_index) { .. }
But only one person has commented on this so far.
Regards,
Brian.
Ah, I was just going on "ri Enumerator", which shows only SyncEnumerator.
<< Rummages around >> - OK, so this is a C extension, which like most of
them, is undocumented in a running system. Pulling down the source though, I
find ext/enumerator/enumerator.txt
Perhaps someone would like to rdoc-merge this into the source?
Regards,
Brian.
[1]; obj.enum(:each_with_index).inject(0) {|sum,(a,i)| sum + a*i}
[2]; i=-1; obj.inject(0) {|sum, a| sum + a*(i+=1)}
Can anyone shade me in on what, if any, advantage [1] has over [2]:
-- in terms of:
a) Readability
b) Ease of use and understanding
c) Performance
d) Flexibility
e) Accuracy
f) Memory usage
g) Anything else you care to throw in ;-)
I can tell at a glance that it's going to require more typing
because it contains the string '_with_index' ...
## keystrokes (including shifts on UK kbd)
# _with_index #-> 11/13
# i=-1; i+=1; #-> 10/11
But there seem to be so many other overheads in comparison,
that I can't focus on whether or not you're saying something
that I need to get my head around.
TIA,
daz
Nobu has transformed enumerator.c -- Try the latest:
http://tinyurl.com/7ljdw/ext/enumerator/enumerator.c?rev=1.6;content-type=text%2Fplain
daz
1. It retains the enapsulation of "iterating with an index"; you are
reimplementing it in your inner block. (Violation of DRY principle).
2. It's more general; it works with other iteration methods. Consider
Hash#each_value and Hash#each_key, or String#each_byte for example.
require 'enumerator'
"abc".to_enum(:each_byte).collect #=> [97, 98, 99]
Of course you could rewrite this too, but then you're reimplementing
'collect':
res = []
"abc".each_byte { |x| res << x }
res
3. If you were using the index in more than one place in the inner block, it
would become more verbose.
i=0; obj.inject(0) { ... use i ... use i ...; i+=1 }
Regards,
Brian.
I've long since started renaming them to ewi and mwi in my own code.
martin
OK. That's why I want it provided automatically inside the block.
It's information known to, but not revealed by, the iterator. We're the
programmers ... they're our blocks ... why can't we discover a fundamental
detail like which iteration we're on ??
> you are reimplementing it in your inner block.
Both examples implement an index. One as an external variable, the other
as an extra block parameter provided by #each_with_index.
I can't see anything I've reimplemented. We may be at cross-purposes ?
> (Violation of DRY principle).
Not so. There's no index, and I need one, so I have to create it.
So do you. It's no more a violation of DRY than having to
open a file _every_ flippin' time I want to read it.
> 2. It's more general; it works with other iteration methods. Consider
> Hash#each_value and Hash#each_key, or String#each_byte for example.
>
I've considered. I can't think of any that it wouldn't work with.
Is there anything more general than that ?
> require 'enumerator'
> "abc".to_enum(:each_byte).collect #=> [97, 98, 99]
>
> Of course you could rewrite this too, but then you're reimplementing
> 'collect':
>
> res = []
> "abc".each_byte { |x| res << x }
> res
OK, _that_ would be a reimplementation of 'collect' but I can't respond
with an example of what I'd do because you haven't used an index.
I'm talking index only, albeit within a slightly expanded topic.
However, dawning is in progress, perhaps :-)
# "abc".to_enum(:each_byte).collect #=> [97, 98, 99]
I was about to describe the #each_byte block as a "proxy receiver"
for #collect, but I think I'll wait before spreading untruths
because it feels reversed (?)
>
> 3. If you were using the index in more than one place in the inner block, it
> would become more verbose.
>
> i=0; obj.inject(0) { ... use i ... use i ...; i+=1 }
>
Absurd ;-)
If your block had 30 uses of the index, my version would have 31.
I'll say again - we may be mixing subjects (accessing an index
versus Enumeration and it's probably just me who doesn't know the
difference, yet).
>
> Brian.
>
Thanks BC,
daz
The documentation in enumerator.txt has not been merged in. How is it
different functionally?
I've diffed this file against ruby-1.8.2 and I see a number of changes but I
don't know what they're doing. Although I do see a couple of nice buffer
overflows :-)
+ VALUE args[2];
+ args[0] = obj;
+ args[1] = sym_each_slice;
+ args[2] = n;
Doh!
Regards,
Brian.
At Thu, 14 Jul 2005 18:02:53 +0900,
Brian Candler wrote in [ruby-talk:148087]:
> > Nobu has transformed enumerator.c -- Try the latest:
> > http://tinyurl.com/7ljdw/ext/enumerator/enumerator.c?rev=1.6;content-type=text%2Fplain
>
> The documentation in enumerator.txt has not been merged in. How is it
> different functionally?
Moved it to the top level and merged the document.
> I've diffed this file against ruby-1.8.2 and I see a number of changes but I
> don't know what they're doing. Although I do see a couple of nice buffer
> overflows :-)
>
> + VALUE args[2];
> + args[0] = obj;
> + args[1] = sym_each_slice;
> + args[2] = n;
>
> Doh!
Thank you, but the code has been rewritten. ;-)
--
Nobu Nakada
At Fri, 15 Jul 2005 14:46:39 +0900,
nobuyoshi nakada wrote in [ruby-talk:148205]:
> At Thu, 14 Jul 2005 18:02:53 +0900,
> Brian Candler wrote in [ruby-talk:148087]:
> > > Nobu has transformed enumerator.c -- Try the latest:
> > > http://tinyurl.com/7ljdw/ext/enumerator/enumerator.c?rev=1.6;content-type=text%2Fplain
> >
> > The documentation in enumerator.txt has not been merged in. How is it
> > different functionally?
>
> Moved it to the top level and merged the document.
But I found rdoc can't handle a module/class enclosed in a
built-in module/class.
enum.c: m..........................
No definition for enum_join
enumerator.c: c
Enclosing class/module 'rb_mEnumerable' for class Enumerator not known
............
Index: lib/rdoc/parsers/parse_c.rb
===================================================================
RCS file: /cvs/ruby/src/ruby/lib/rdoc/parsers/parse_c.rb,v
retrieving revision 1.30
diff -U2 -p -r1.30 parse_c.rb
--- lib/rdoc/parsers/parse_c.rb 14 May 2005 02:23:45 -0000 1.30
+++ lib/rdoc/parsers/parse_c.rb 15 Jul 2005 14:30:23 -0000
@@ -228,4 +228,11 @@ module RDoc
enclosure = @classes[in_module]
unless enclosure
+ if enclosure = @known_classes[in_module]
+ handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
+ enclosure, nil, nil)
+ enclosure = @classes[in_module]
+ end
+ end
+ unless enclosure
warn("Enclosing class/module '#{in_module}' for " +
"#{class_mod} #{class_name} not known")
--
Nobu Nakada