[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Mini-RCR: Extra Argument for Array#join

James Gray

12/31/2006 8:05:00 PM

I've been looking at the to_sentence() method in Rails. It's
basically a join(), but you can give a different final separator. I
personally use this kind of functionality often enough to believe it
would make a good addition to the core language.

Even better, I think we can make join() smart enough to eliminate any
need for to_sentence() by adding a second argument. Here's a sample
implementation:

#!/usr/bin/env ruby -w

require "enumerator"

class Array
alias_method :old_join, :join
def join(sep = $,, last_sep = sep)
return "" if empty?
enum_with_index.inject("") do |str, (e, i)|
"#{str}#{i == size - 1 ? last_sep : sep}#{e}"
end[sep.to_s.length..-1]
end
end

if __FILE__ == $PROGRAM_NAME
require "test/unit"

class TestJoin < Test::Unit::TestCase
def test_old_matches_new
assert_equal([].old_join, [].join)
assert_equal([1].old_join, [1].join)
assert_equal([1, 2].old_join, [1, 2].join)
assert_equal((1..5).to_a.old_join, (1..5).to_a.join)

assert_equal([].old_join("|"), [].join("|"))
assert_equal([1].old_join("|"), [1].join("|"))
assert_equal([1, 2].old_join("|"), [1, 2].join("|"))
assert_equal((1..5).to_a.old_join("|"), (1..5).to_a.join("|"))
end

def test_new_last_arg_behavior
assert_equal("1, 2, 3, 4, and 5", (1..5).to_a.join(", ", ",
and "))
assert_equal("1, 2, 3, 4 and 5", (1..5).to_a.join(", ", " and
"))
assert_equal("1, 2, 3, 4 & 5", (1..5).to_a.join(", ", " & "))

assert_equal([1, 2].join(","), [1, 2].join("ignored", ","))
end
end
end

__END__

Does anyone else like this?

James Edward Gray II

12 Answers

Gregory Brown

12/31/2006 8:16:00 PM

0

On 12/31/06, James Edward Gray II <james@grayproductions.net> wrote:

> Does anyone else like this?

It's neat. I think it's better than to_sentence.

But maybe not something necessary for core.

I tend to think of join() to be a delimiter-generator, and not an
english language generator.

But that's just my two cents. Wouldn't be opposed to an adjustment,
but personally have never needed it for my day to day Ruby usage.

Gavin Kistner

12/31/2006 8:20:00 PM

0

James Edward Gray II wrote:
> I've been looking at the to_sentence() method in Rails. It's
> basically a join(), but you can give a different final separator. I
> personally use this kind of functionality often enough to believe it
> would make a good addition to the core language.

What makes the final separator so special that it deserves inclusion,
while making no allowance for a pre-separator? Just the English
language?

(At the same time as I ask this, I'm wondering if asking for
completeness for the sake of completeness, without a corresponding use
case, is the sort of foolish consistency that is the hobgoblin of
little minds.)

Devin Mullins

12/31/2006 9:31:00 PM

0

Phrogz wrote:
> What makes the final separator so special that it deserves inclusion,
> while making no allowance for a pre-separator? Just the English
> language?

Well, just for poops and giggles, here's a less racist alternative
(JEG-compatible):

require "enumerator"

class Array
alias old_join join
def join(sep = $,, last_sep = nil, &each_prefix)
each_prefix ||= proc do |e,i|
(last_sep if i == length - 1) || sep
end
enum_with_index.map do |e,i|
i == 0 ? e : "#{each_prefix[e,i]}#{e}"
end.old_join
end
end

if __FILE__ == $PROGRAM_NAME
require "test/unit"

class TestJoin < Test::Unit::TestCase
def test_old_matches_new
assert_equal([].old_join, [].join)
assert_equal([1].old_join, [1].join)
assert_equal([1, 2].old_join, [1, 2].join)
assert_equal((1..5).to_a.old_join, (1..5).to_a.join)

assert_equal([].old_join("|"), [].join("|"))
assert_equal([1].old_join("|"), [1].join("|"))
assert_equal([1, 2].old_join("|"), [1, 2].join("|"))
assert_equal((1..5).to_a.old_join("|"), (1..5).to_a.join("|"))
end

def test_new_last_arg_behavior
assert_equal("1, 2, 3, 4, and 5", (1..5).to_a.join(", ", ", and "))
assert_equal("1, 2, 3, 4 and 5", (1..5).to_a.join(", ", " and "))
assert_equal("1, 2, 3, 4 & 5", (1..5).to_a.join(", ", " & "))

assert_equal([1, 2].join(","), [1, 2].join("ignored", ","))
end

def test_new_block_behavior
assert_equal '1 dna, 2, 3, 4, 5',
(1..5).to_a.join {|e,i| i == 1 ? ' dna, ' : ', ' }
end
end
end

Gregory Brown

12/31/2006 9:38:00 PM

0

On 12/31/06, Devin Mullins <twifkak@comcast.net> wrote:

> def test_new_block_behavior
> assert_equal '1 dna, 2, 3, 4, 5',
> (1..5).to_a.join {|e,i| i == 1 ? ' dna, ' : ', ' }
> end


Hmm... this seems powerful and generally useful.

Ryan Davis

12/31/2006 10:17:00 PM

0


On Dec 31, 2006, at 12:05 PM, James Edward Gray II wrote:

> I've been looking at the to_sentence() method in Rails. It's
> basically a join(), but you can give a different final separator.
> I personally use this kind of functionality often enough to believe
> it would make a good addition to the core language.
>
> Even better, I think we can make join() smart enough to eliminate
> any need for to_sentence() by adding a second argument. Here's a
> sample implementation:
>
> #!/usr/bin/env ruby -w
>
> require "enumerator"
>
> class Array
> alias_method :old_join, :join
> def join(sep = $,, last_sep = sep)
> return "" if empty?
> enum_with_index.inject("") do |str, (e, i)|
> "#{str}#{i == size - 1 ? last_sep : sep}#{e}"
> end[sep.to_s.length..-1]
> end
> end

problems:

1) calculating a static value inside a loop. (only costs about a
hundredth of a second for 10k)
2) iterating when 1/2 of your values are completely static.
3) being clever by using enumerator.

class Array
alias_method :old_join, :join
def join(sep = $,, last_sep = sep)
return "" if empty?

seperators = Array.new(size-1, sep)
seperators[-1] = last_sep unless seperators.empty?
self.zip(seperators).old_join
end
end

# of iterations = 1000000
user system total real
null_time 0.140000 0.000000 0.140000 ( 0.140482)
jeg 29.690000 0.080000 29.770000 ( 30.144816)
ryan 19.780000 0.050000 19.830000 ( 19.998077)



James Gray

12/31/2006 10:22:00 PM

0

On Dec 31, 2006, at 4:16 PM, Ryan Davis wrote:

>
> On Dec 31, 2006, at 12:05 PM, James Edward Gray II wrote:
>
>> I've been looking at the to_sentence() method in Rails. It's
>> basically a join(), but you can give a different final separator.
>> I personally use this kind of functionality often enough to
>> believe it would make a good addition to the core language.
>>
>> Even better, I think we can make join() smart enough to eliminate
>> any need for to_sentence() by adding a second argument. Here's a
>> sample implementation:
>>
>> #!/usr/bin/env ruby -w
>>
>> require "enumerator"
>>
>> class Array
>> alias_method :old_join, :join
>> def join(sep = $,, last_sep = sep)
>> return "" if empty?
>> enum_with_index.inject("") do |str, (e, i)|
>> "#{str}#{i == size - 1 ? last_sep : sep}#{e}"
>> end[sep.to_s.length..-1]
>> end
>> end
>
> problems:
>
> 1) calculating a static value inside a loop. (only costs about a
> hundredth of a second for 10k)
> 2) iterating when 1/2 of your values are completely static.
> 3) being clever by using enumerator.

This was intended as a point of discussion, not my final offer as the
ideal implementation. Thank you for cleaning it up though.

James Edward Gray II


Trans

1/1/2007 4:39:00 PM

0


Ryan Davis wrote:
> class Array
> alias_method :old_join, :join
> def join(sep = $,, last_sep = sep)
> return "" if empty?
>
> seperators = Array.new(size-1, sep)
> seperators[-1] = last_sep unless seperators.empty?
> self.zip(seperators).old_join
> end
> end
>
> # of iterations = 1000000
> user system total real
> null_time 0.140000 0.000000 0.140000 ( 0.140482)
> jeg 29.690000 0.080000 29.770000 ( 30.144816)
> ryan 19.780000 0.050000 19.830000 ( 19.998077)

I wonder how this would fair.

class Array
alias :old_join :join
def join( sep=$,, last_sep=nil )
s = old_join(sep)
if last_sep
rsep = Regexp.escape(sep.to_s)
rlast = Regexp.escape(last.to_s)
s.sub!(/#{rsep}#{rlast}$/,"#{last_sep}#{last}")
end
return s
end
end

Sorry, the bencmark script wasn't posted and I didn't feel like
recreating it.

T.

S. Robert James

1/2/2007 1:13:00 AM

0

James Gray wrote:
> I've been looking at the to_sentence() method in Rails. It's
> basically a join(), but you can give a different final separator. I
> personally use this kind of functionality often enough to believe it
> would make a good addition to the core language.

-1
join is simple to understand and use. It's used in lots of things
besides English generation. Adding this complicates it and confuses the
intention.

If you feel to_sentence() should be made core, put up a RCR for it. But
let's keep join as join.

--
Posted via http://www.ruby-....

Ryan Davis

1/3/2007 2:34:00 AM

0


On Jan 1, 2007, at 8:40 AM, Trans wrote:

> I wonder how this would fair.
>
> class Array
> alias :old_join :join
> def join( sep=$,, last_sep=nil )
> s = old_join(sep)
> if last_sep
> rsep = Regexp.escape(sep.to_s)
> rlast = Regexp.escape(last.to_s)
> s.sub!(/#{rsep}#{rlast}$/,"#{last_sep}#{last}")
> end
> return s
> end
> end
>
> Sorry, the bencmark script wasn't posted and I didn't feel like
> recreating it.

% ./blah.rb 1_000_000
# of iterations = 1000000
user system total real
null_time 0.140000 0.000000 0.140000 ( 0.139480)
ryan 19.760000 0.020000 19.780000 ( 19.810269)
trans 21.290000 0.040000 21.330000 ( 21.398592)
Loaded suite ./blah
Started
....
Finished in 0.001109 seconds.

4 tests, 24 assertions, 0 failures, 0 errors



Trans

1/3/2007 3:43:00 AM

0


Ryan Davis wrote:
> On Jan 1, 2007, at 8:40 AM, Trans wrote:
>
> > I wonder how this would fair.
> >
> > class Array
> > alias :old_join :join
> > def join( sep=$,, last_sep=nil )
> > s = old_join(sep)
> > if last_sep
> > rsep = Regexp.escape(sep.to_s)
> > rlast = Regexp.escape(last.to_s)
> > s.sub!(/#{rsep}#{rlast}$/,"#{last_sep}#{last}")
> > end
> > return s
> > end
> > end
> >
> > Sorry, the bencmark script wasn't posted and I didn't feel like
> > recreating it.
>
> % ./blah.rb 1_000_000
> # of iterations = 1000000
> user system total real
> null_time 0.140000 0.000000 0.140000 ( 0.139480)
> ryan 19.760000 0.020000 19.780000 ( 19.810269)
> trans 21.290000 0.040000 21.330000 ( 21.398592)
> Loaded suite ./blah
> Started
> ....
> Finished in 0.001109 seconds.
>
> 4 tests, 24 assertions, 0 failures, 0 errors

Eek. That was worse then I thought it would be. Thanks for showing me
though, Ryan.

Of course now that I'm looking at it again I'm wondering how we missed:

class Array
def join( sep=$,, last_sep=nil )
return old_join(sep) unless last_sep
[slice(0...-1).old_join(sep), last].old_join(last_sep)
end
end

That must to be faster.

T.