[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Re: Determining the common prefix for several strings

ronald braswell

8/3/2007 4:24:00 AM




>From: Xavier Noria <fxn@hashref.com>
>Reply-To: ruby-talk@ruby-lang.org
>To: ruby-talk@ruby-lang.org (ruby-talk ML)
>Subject: Re: Determining the common prefix for several strings
>Date: Fri, 3 Aug 2007 12:22:45 +0900
>
>El Aug 3, 2007, a las 4:46 AM, Xavier Noria escribió:
>
>> splits = items.map {|i| i.split(//)}
>> (0...splits.length).each do |i|
>> if splits.map {|s| s[i]}.uniq.length != 1
>> puts items.first[0, i]
>> break
>> end
>> end
>
>There's a bug in the upper limit of the range, it should be the length of
>some element of the splits
>
> splits = items.map {|i| i.split(//)}
> (0...splits.first.length).each do |i|
> if splits.map {|s| s[i]}.uniq.length != 1
> puts items.first[0, i]
> break
> end
> end
>
>We could compute the length of the shortest string and use that finer
>upper limit, but that's not necessary because even in the case that the
>shortest string is the common prefix, we would get some nil in the map
>through columns in the next iteration, and thus at least two distinct
>values. On the other hand there's the corner case when all strings are
>equal, that's not handled in the above code because they way to do it
>depends on the details.
>
>Please forgive those many revisions, I guess I need some sleep at 5:22 AM.
>
>-- fxn
>
>

Here is yet another way to solve this problem.

min_len = items.min{ |i,j| i.length <=> j.length }.length
common_prefix = ""
(0...min_len).each do |i|
col = items.inject(""){ |sum,elem| sum << elem[i] }.squeeze
if col.length > 1
puts common_prefix
break
end
common_prefix << col
end

Ron

_________________________________________________________________
Find a local pizza place, movie theater, and more?.then map the best route!
http://maps.live.com/default.aspx?v=2&ss=yp.bars~yp.pizza~yp.movie%20theater&cp=42.358996~-71.056691&style=r&lvl=13&tilt=-90&dir=0&alt=-1000&scene=950607&encType=1&F...


8 Answers

Todd Burch

8/3/2007 5:25:00 AM

0

Holy Cow! You guys are geniuses! Y'all are using these methods like
I've never seen or even thought of using them before.

Problem is solved - script is working - THANKS!!!

Todd

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

Peña, Botp

8/3/2007 7:58:00 AM

0

On Behalf Of Todd Burch:
# Holy Cow! You guys are geniuses! Y'all are using these
# methods like. I've never seen or even thought of using them before.

don’t count me pls. i'm not holy nor genius, (possibly a cow :) but just a nuby learning ruby fr the marvelous ruby community (thanks to matz, nobu, guydecoux, mauricio, dblack, ...ml-list.size ).

ff is another example (of my nubiness). it shows niftiness of bang methods, chains, and the underutilized all?,

CC:\family\ruby>cat test.rb
list = %w(itemone itemtwo item222 item0033 item11 itexthis itemthisandthat itemt
hose)

min = list.sort!{|a,b| a.size <=> b.size}.first
break if list.all?{|a| a =~ /^#{min}/} while min.chop!

puts min
C:\family\ruby>ruby test.rb
ite

C:\family\ruby>

i luv my family. i luv ruby :)

kind regards -botp

Simon Kröger

8/3/2007 10:21:00 PM

0

Peña wrote:
> [...]
> ff is another example (of my nubiness). it shows niftiness of bang methods, chains, and the underutilized all?,

don't forget the any? :)

puts r.first[0...(0..r.first.size).find{|i| r.any?{|s|r.first[0..i]!=s[0..i]}}]

cheers

Simon

Gavin Sinclair

8/6/2007 7:25:00 AM

0

On Aug 3, 5:58 pm, Pe?a, Botp <b...@delmonte-phil.com> wrote:
>
> min = list.sort!{|a,b| a.size <=> b.size}.first
> break if list.all?{|a| a =~ /^#{min}/} while min.chop!
>

I just realised that the solution I just posted elsewhere in this
thread is very very similar to this one.

Be careful that using bang methods in chains is sometimes problematic,
as they can return nil if nothing changes (sort! doesn't have this
problem). For interested readers, find the problem with this code:

title = "the moon in june"
title.downcase!.capitalize!

Cheers,
Gavin

Peña, Botp

8/6/2007 8:42:00 AM

0

From: Gavin Sinclair [mailto:gsinclair@gmail.com]
# On Aug 3, 5:58 pm, Peña, Botp <b...@delmonte-phil.com> wrote:
# > min = list.sort!{|a,b| a.size <=> b.size}.first
# > break if list.all?{|a| a =~ /^#{min}/} while min.chop!
# I just realised that the solution I just posted elsewhere in this
# thread is very very similar to this one.
# Be careful that using bang methods in chains is sometimes problematic,
# as they can return nil if nothing changes (sort! doesn't have this
# problem).

indeed. as long as the array has no nil elements, w're ok w sort!

irb(main):009:0> s="test"
=> "test"
irb(main):010:0> s.downcase!.downcase!
NoMethodError: undefined method `downcase!' for nil:NilClass
from (irb):10
from :0
irb(main):011:0> a=[1,1,1]
=> [1, 1, 1]
irb(main):012:0> a.sort!.sort!
=> [1, 1, 1]

i realized just now that i posted the first code. That should be,

min = list.sort!{|a,b| a.size <=> b.size}.shift # <--shift instead of first
break if list.all?{|a| a =~ /^#{min}/} while min.chop!

but anyway, after a couple of tests, the sort does not help much, so

min = list.min{|a,b| a.size <=> b.size}
break if list.all?{|a| a =~ /^#{min}/} while min.chop!

hmm.. Gavin, your until code just gave me a hint. if we reverse logic, we can save the break and make it more readable too.

min = list.min{|a,b| a.size <=> b.size}
min.chop! until list.all?{|a| a =~ /^#{min}/}

and we can use switch w #any? anytime too :)

min = list.min{|a,b| a.size <=> b.size}
min.chop! while list.any?{|a| a !~ /^#{min}/}

ruby power, indeed.

kind regards -botp


Robert Klemme

8/6/2007 11:50:00 AM

0

2007/8/6, Peña, Botp <botp@delmonte-phil.com>:
> From: Gavin Sinclair [mailto:gsinclair@gmail.com]
> # On Aug 3, 5:58 pm, Peña, Botp <b...@delmonte-phil.com> wrote:
> # > min = list.sort!{|a,b| a.size <=> b.size}.first
> # > break if list.all?{|a| a =~ /^#{min}/} while min.chop!
> # I just realised that the solution I just posted elsewhere in this
> # thread is very very similar to this one.
> # Be careful that using bang methods in chains is sometimes problematic,
> # as they can return nil if nothing changes (sort! doesn't have this
> # problem).
>
> indeed. as long as the array has no nil elements, w're ok w sort!
>
> irb(main):009:0> s="test"
> => "test"
> irb(main):010:0> s.downcase!.downcase!
> NoMethodError: undefined method `downcase!' for nil:NilClass
> from (irb):10
> from :0
> irb(main):011:0> a=[1,1,1]
> => [1, 1, 1]
> irb(main):012:0> a.sort!.sort!
> => [1, 1, 1]
>
> i realized just now that i posted the first code. That should be,
>
> min = list.sort!{|a,b| a.size <=> b.size}.shift # <--shift instead of first
> break if list.all?{|a| a =~ /^#{min}/} while min.chop!
>
> but anyway, after a couple of tests, the sort does not help much, so
>
> min = list.min{|a,b| a.size <=> b.size}
> break if list.all?{|a| a =~ /^#{min}/} while min.chop!
>
> hmm.. Gavin, your until code just gave me a hint. if we reverse logic, we can save the break and make it more readable too.
>
> min = list.min{|a,b| a.size <=> b.size}
> min.chop! until list.all?{|a| a =~ /^#{min}/}
>
> and we can use switch w #any? anytime too :)
>
> min = list.min{|a,b| a.size <=> b.size}
> min.chop! while list.any?{|a| a !~ /^#{min}/}
>
> ruby power, indeed.

All these solutions have the issue that they modify the list which
might or might not be ok. I tend to prefer to view the item list as
read only. So I'd do:

min = list.min{|a,b| a.size <=> b.size}.dup
min.chop! until list.all?{|a| a =~ /^#{min}/}

I have added some interesting benchmarking:

$ ./prefix.rb
user system total real
1000 * list.rx 0.016000 0.000000 0.016000 ( 0.014000)
1000 * list.rx with conversion 0.031000 0.000000 0.031000 ( 0.030000)
1000 * list.one_pass 0.219000 0.000000 0.219000 ( 0.218000)
1000 * list.min 0.328000 0.000000 0.328000 ( 0.337000)
1000 * list.first.dup 0.640000 0.000000 0.640000 ( 0.641000)
1 * large.rx 0.000000 0.000000 0.000000 ( 0.000000)
1 * large.rx with conversion 0.000000 0.000000 0.000000 ( 0.000000)
1 * large.one_pass 25.985000 0.000000 25.985000 ( 26.298000)
1 * large.min 34.953000 0.000000 34.953000 ( 35.098000)
1 * large.first.dup 27.719000 0.000000 27.719000 ( 27.732000)


(see attachment for code)

Kind regards

robert

Peña, Botp

8/7/2007 1:17:00 AM

0

From: Robert Klemme [mailto:shortcutter@googlemail.com]
# I have added some interesting benchmarking:
#
# $ ./prefix.rb
# user system total real
# 1000 * list.rx 0.016000 0.000000 0.016000 ( 0.014000)
# 1000 * list.rx with conversion 0.031000 0.000000 0.031000 ( 0.030000)
# 1 * large.rx 0.000000 0.000000 0.000000 ( 0.000000)
# 1 * large.rx with conversion 0.000000 0.000000 0.000000 ( 0.000000)

Robert, thanks for the benchmark. I included Brian|Martin's solution in the mix but still the regex scheme is way a lot better w regards to speed.

Could some kind soul enlighten me why the regex solution works better, and even best using large strings?

kind regards -botp

Robert Klemme

8/7/2007 5:12:00 AM

0

On 07.08.2007 03:16, Peña wrote:
> From: Robert Klemme [mailto:shortcutter@googlemail.com]
> # I have added some interesting benchmarking:
> #
> # $ ./prefix.rb
> # user system total real
> # 1000 * list.rx 0.016000 0.000000 0.016000 ( 0.014000)
> # 1000 * list.rx with conversion 0.031000 0.000000 0.031000 ( 0.030000)
> # 1 * large.rx 0.000000 0.000000 0.000000 ( 0.000000)
> # 1 * large.rx with conversion 0.000000 0.000000 0.000000 ( 0.000000)
>
> Robert, thanks for the benchmark. I included Brian|Martin's solution in the mix but still the regex scheme is way a lot better w regards to speed.
>
> Could some kind soul enlighten me why the regex solution works better, and even best using large strings?

My theory is that it's completely done in C and there is only *one*
invocation. All other solutions need some form of iteration implemented
in Ruby.

Kind regards

robert