From: GGarramuno@... (GGarramuno) Date: 2004-01-20T04:45:01+09:00 Subject: Re: Fighting Ruby's bad fame Gavin Sinclair wrote in message news:<33606556942.20040120002034@soyabean.com.au>... > On Monday, January 19, 2004, 1:05:02 PM, GGarramuno wrote: > > > I've been using ruby for about 1 week & 1/2 and already run into that. I > > created String#expand_tabs() function in a module of mine only to find a > > similar (but less efficient implementation) of that function in another popular > > ruby library. > > Could you send me the efficient implementation? :) > Sure, here it is. This implementation will perform a tiny bit slower on short documents that are mainly separated by a single tab, but it should perform dramatically faster in longer documents, specially those with multiple contiguous tabs (last test, being the most pathologial one). Of course, the best thing would be to move these methods from ruby onto C, if you ask me: require "benchmark" class String # Return new string with all tabs set to spaces def new_expand_tabs(n=8) n = n.to_int raise ArgumentError, "n must be >= 0" if n < 0 return gsub(/\t/, '') if n == 0 return gsub(/\t/, ' ') if n == 1 h = self.dup while h.gsub!(/^([^\t\n]*)(\t+)/) { |f| val = ( n * $2.size - ($1.size % n) ) $1 << (' ' * val) } end h end def old_expand_tabs(n=8) n = n.to_int raise ArgumentError, "n must be >= 0" if n < 0 return gsub(/\t/, "") if n == 0 return gsub(/\t/, " ") if n == 1 str = self.dup while str =~ /\t/ str.gsub!(/(^.*?)\t/) { |s| # Our match starts at the beginning of a line, so we count the # characters before the tab, and expand the tab to the next multiple # of N spaces. width = $1.size gap = n - (width % n) # Replace the tab with 'gap' spaces. $1 + (" " * gap) } end str end end ### Dummy tests n = 100000 s1 = "\t\t\t\t\t\tadssaa\t\t\t\t\t" * 1000 s2 = "\t\t\t\t\t\tadssaa\t\t\t\t\t\n\n\t\t\n" * n s3 = "adssaa\t\t\t\t\t\nxxxxxxx\t\t\nyy\n" * n s4 = "adssaa\t\t\t\t\txxxxxxx\n" * n s5 = "\na\ta\t\a\ta\t\a\n" * n s6 = "\na\tthis\t\is\ta\t\test\tof\thow\tgood\ttabulation\tis.\n" * n t1 = '' t3 = '' Benchmark.bm { |x| x.report("n ") { t3 = s2.new_expand_tabs(); } x.report("o ") { t1 = s2.old_expand_tabs(); } } if ( t3 != t1 ) t3.gsub!(/\t/,'-') t1.gsub!(/\t/,'-') puts "new error!",t3,".",t1 end Benchmark.bm { |x| x.report("n ") { t3 = s3.new_expand_tabs(); } x.report("o ") { t1 = s3.old_expand_tabs(); } } puts "new error!" if ( t3 != t1 ) Benchmark.bm { |x| x.report("n ") { t3 = s4.new_expand_tabs(); } x.report("o ") { t1 = s4.old_expand_tabs(); } } puts "new error!" if ( t3 != t1 ) Benchmark.bm { |x| x.report("n ") { t3 = s5.new_expand_tabs(); } x.report("o ") { t1 = s5.old_expand_tabs(); } } puts "new error!" if ( t3 != t1 ) Benchmark.bm { |x| x.report("n ") { t3 = s6.new_expand_tabs(); } x.report("o ") { t1 = s6.old_expand_tabs(); } } puts "new error!" if ( t3 != t1 ) Benchmark.bm { |x| x.report("n ") { t3 = s1.new_expand_tabs(); } x.report("o ") { t1 = s1.old_expand_tabs(); } } puts "new error!" if ( t3 != t1 ) On my system: user system total real n 8.422000 0.080000 8.502000 ( 8.602000) o 53.096000 0.420000 53.516000 ( 53.858000) user system total real n 4.807000 0.050000 4.857000 ( 4.887000) o 20.560000 0.151000 20.711000 ( 20.830000) user system total real n 2.423000 0.050000 2.473000 ( 2.483000) o 13.049000 0.080000 13.129000 ( 13.270000) user system total real n 9.293000 0.060000 9.353000 ( 9.403000) o 9.324000 0.080000 9.404000 ( 9.434000) user system total real n 26.097000 0.250000 26.347000 ( 26.498000) o 40.969000 0.391000 41.360000 ( 41.529000) user system total real n 14.721000 5.678000 20.399000 ( 20.570000) o 228.529000 3.365000 231.894000 (235.588000) > What's so bad about name clashes? Nothing much. Just that it is something I wouldn't be giving any thought with python or perl, but that you tell me I may have to watch out it for in ruby. > And, > short of clashing with a library whose explicit purpose is to extend > the built-in classes, I bet you don't even find any name clashes. :) > Not sure I agree. Do you know how many vector, matrix and so forth libraries are out there? All of them more or less incompatible with each other? > I don't like the sound of that "feature". A well-designed library > should be loaded in full. In OO languages especially, you either load > a class or you don't; you don't pick and choose methods. > Personally, I'm not too interested in choosing methods, but I am in choosing classes. Also, think of it negatively, as I do. What if the library you have to use was not well designed? What if this is found out only in the middle of the project? What if the person maintaining the library has no time on their hands to fix it (or worse, refuses to change it for some other reason)? Do I just hack into the library and start maintaining my own incompatible version which I would then have to keep updating every time a new update to the master library is released? Between being tied and just going on my own tangent, I'd rather take the comprise of just picking and choosing what I want to use... without hacking into the original code. Will I'll be using that feature often? Heck, I hope never! But I'll sleep well at nights knowing that if I ever HAVE to, that feature is already there. > > Personally I like this phisolophy of ruby, but I NEED to have the ability to > > easily keep changes to base classes local to my class or in an easy way to > > revert them. > > At the risk of re-igniting an old flamewar, no you don't. Not until > you prove that you need it anyway. At the moment it's mere paranoia. > Paranoia is good in programming. Keeps you sane later on. And this fear must be a plague, as searching for solutions in the ruby world, I've seen at least 3 or 4 proposals trying to address this same issue, including: - behaviors - matz's namespaces - profile method visibility That tells me that: a) it is a problem already or b) it is enough of a potential future problem to merit a solution. Personally, I prefer the namespaces approach, but any of them standard in the language would likely be better than having none if you ask me.