[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

join not in Enumerable

Logan Capaldo

5/21/2005 10:35:00 PM

Just a few minutes ago I was playing with irb as I am wont to do, and
typed this:

('a'..'z').join(' ')

Lo and behold it protested at me with a NoMethodError. I said to my
self, self there is no reason that has to be Array only functionality.
Why isn't it in Enumerable? So I said:

module Enumerable
def join(sep = '')
inject do |a, b|
"#{a}#{sep}#{b}"
end
end
end

And then I said ('a'..'z').join(' ') and got:
=> "a b c d e f g h i j k l m n o p q r s t u v w x y z"

#inject has to be the most dangerously effective method ever. But I digress:

Why is join, and perhaps even pack in Array and not in Enumerable?


13 Answers

Ara.T.Howard

5/21/2005 11:56:00 PM

0

dblack

5/22/2005 2:05:00 AM

0

Jim Weirich

5/22/2005 4:38:00 AM

0

On Saturday 21 May 2005 10:05 pm, David A. Black wrote:
> Hi --
>
> On Sun, 22 May 2005, Logan Capaldo wrote:
> > Just a few minutes ago I was playing with irb as I am wont to do, and
> > typed this:
> >
> > ('a'..'z').join(' ')
> >
> > Lo and behold it protested at me with a NoMethodError. I said to my
> > self, self there is no reason that has to be Array only functionality.
> > Why isn't it in Enumerable? So I said:
> >
> > module Enumerable
> > def join(sep = '')
> > inject do |a, b|
> > "#{a}#{sep}#{b}"
> > end
> > end
> > end
> >
> > And then I said ('a'..'z').join(' ') and got:
> > => "a b c d e f g h i j k l m n o p q r s t u v w x y z"
> >
> > #inject has to be the most dangerously effective method ever. But I
> > digress:
>
> You can speed it up a lot if you do this:

[... elided version using to_a ...]

The reason the non-to_a version is slow is because it creates a series of
increasingly larger strings. A faster version (without resorting to to_a)
would build up a single string gradually. Here is another version:

def join(sep='')
inject(nil) { |a, b|
a ? (a << sep << b.to_s) : "#{b}"
}
end

Here are the timings I got ...

user system total real
to_a: 0.580000 0.000000 0.580000 ( 0.583975)
inject slow: 10.520000 0.210000 10.730000 ( 11.998484)
inject fast: 0.590000 0.020000 0.610000 ( 0.651972)

--
-- Jim Weirich jim@weirichhouse.org http://onest...
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)


Christoph R.

5/22/2005 8:22:00 AM

0

Jim Weirich schrieb:

>
> def join(sep='')
> inject(nil) { |a, b|
> a ? (a << sep << b.to_s) : "#{b}"
> }
> end
>
>
It's
p ([].join) # ""

so this should be

def join(sep="")
if sep == ""
inject('') { |a, b|
a << b.inspect
}
else
inject('') { |a, b|
a << sep << b.inspect
}
end
end

/Christoph


nobu.nokada

5/22/2005 9:37:00 AM

0

Hi,

At Sun, 22 May 2005 08:56:08 +0900,
Ara.T.Howard wrote in [ruby-talk:143311]:
> the only reason i can think of is that just because somthing is countable
> (Enumerable) doesn't mean each sub-thing is singular. take a hash for
> example. this is no stubling block (pun intended) for ruby however:

Feels interesting.


Index: enum.c
===================================================================
RCS file: /cvs/ruby/src/ruby/enum.c,v
retrieving revision 1.54
diff -U2 -p -r1.54 enum.c
--- enum.c 30 Oct 2004 06:56:17 -0000 1.54
+++ enum.c 22 May 2005 09:36:21 -0000
@@ -967,4 +967,52 @@ enum_zip(argc, argv, obj)
}

+static VALUE
+enum_join_s(obj, arg, recur)
+ VALUE obj, *arg;
+ int recur;
+{
+ if (recur) {
+ static const char recursed[] = "[...]";
+ if (!NIL_P(arg[1]) && RSTRING(arg[0])->len != 0) {
+ rb_str_append(arg[0], arg[1]);
+ }
+ rb_str_cat(arg[0], recursed, sizeof(recursed) - 1);
+ }
+ else {
+ if (rb_block_given_p()) {
+ obj = rb_yield(obj);
+ }
+ if (TYPE(obj) != T_STRING) {
+ obj = rb_obj_as_string(obj);
+ }
+ if (!NIL_P(arg[1]) && RSTRING(arg[0])->len != 0) {
+ rb_str_append(arg[0], arg[1]);
+ }
+ rb_str_append(arg[0], obj);
+ }
+ return arg[0];
+}
+
+static VALUE
+enum_join_i(el, arg)
+ VALUE el, arg;
+{
+ return rb_exec_recursive(enum_join_s, el, arg);
+}
+
+static VALUE
+enum_join(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE arg[2];
+
+ rb_scan_args(argc, argv, "01", &arg[1]);
+ arg[0] = rb_str_new(0, 0);
+ rb_iterate(rb_each, obj, enum_join_i, (VALUE)arg);
+ return arg[0];
+}
+
/*
* The <code>Enumerable</code> mixin provides collection classes with
@@ -998,4 +1046,5 @@ Init_Enumerable()
rb_define_method(rb_mEnumerable,"inject", enum_inject, -1);
rb_define_method(rb_mEnumerable,"partition", enum_partition, 0);
+ rb_define_method(rb_mEnumerable,"classify", enum_classify, 0);
rb_define_method(rb_mEnumerable,"all?", enum_all, 0);
rb_define_method(rb_mEnumerable,"any?", enum_any, 0);
@@ -1008,4 +1057,5 @@ Init_Enumerable()
rb_define_method(rb_mEnumerable,"each_with_index", enum_each_with_index, 0);
rb_define_method(rb_mEnumerable, "zip", enum_zip, -1);
+ rb_define_method(rb_mEnumerable, "join", enum_join, -1);

id_eqq = rb_intern("===");


--
Nobu Nakada


Kristof Bastiaensen

5/22/2005 9:54:00 AM

0

On Sun, 22 May 2005 11:05:10 +0900, David A. Black wrote:

>> Why is join, and perhaps even pack in Array and not in Enumerable?
>
> I guess to_a makes the conversion pretty easy, and Array tends to
> serve as the "normalized" version of Enumerable in a lot of contexts.
> I don't know if there's any other reason.

I believe because join requires an ordered collection, and enumerables
aren't guaranteed to be ordered. For example the order of traversing a
Hash may differ for a different hash with the same elements. For this
reason the output of join for an enumerable is undefined.

Regards,
KB

Robert Klemme

5/22/2005 10:32:00 AM

0


"Kristof Bastiaensen" <kristof@vleeuwen.org> schrieb im Newsbeitrag
news:pan.2005.05.22.09.53.41.174104@vleeuwen.org...
> On Sun, 22 May 2005 11:05:10 +0900, David A. Black wrote:
>
>>> Why is join, and perhaps even pack in Array and not in Enumerable?
>>
>> I guess to_a makes the conversion pretty easy, and Array tends to
>> serve as the "normalized" version of Enumerable in a lot of contexts.
>> I don't know if there's any other reason.
>
> I believe because join requires an ordered collection, and enumerables
> aren't guaranteed to be ordered.

That would be my answer, too.

> For example the order of traversing a
> Hash may differ for a different hash with the same elements. For this
> reason the output of join for an enumerable is undefined.

At least it is unpredictable. Even more so: order may change completely
with each insertion:

>> h=(0..5).inject({}){|h,i| h[i.to_s]=i;h}
=> {"0"=>0, "1"=>1, "2"=>2, "3"=>3, "4"=>4, "5"=>5}
>> h.to_a
=> [["0", 0], ["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5]]
>> h["6"]=6
=> 6
>> h.to_a
=> [["6", 6], ["0", 0], ["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5]]

Kind regards

robert

dblack

5/22/2005 11:19:00 AM

0

dblack

5/22/2005 11:26:00 AM

0

Ara.T.Howard

5/22/2005 3:13:00 PM

0