From: "Michael W. Ryder" <_mwryder@...> Date: 2007-07-23T11:49:59+09:00 Subject: Re: Is there a replacement for sub? bbiker wrote: > On Jul 21, 11:40 pm, "Michael W. Ryder" <_mwry...@worldnet.att.net> > wrote: >> Bernard Kenik wrote: >>> ruby-talk-ad...@ruby-lang.org wrote: >>>> ------------------------------------------------------------------------ >>>> Subject: >>>> Re: Is there a replacement for sub? >>>> From: >>>> "Robert Dober" >>>> Date: >>>> Fri, 20 Jul 2007 20:49:07 +0900 >>>> To: >>>> ruby-t...@ruby-lang.org (ruby-talk ML) >>>> To: >>>> ruby-t...@ruby-lang.org (ruby-talk ML) >>>> On 7/20/07, Martin DeMello wrote: >>>>> On 7/20/07, Robert Dober wrote: >>>>>> I was reading this whole thread and I kind of think to be dreaming, I >>>>>> must have missed something obvious!!! >>>>>> Anyway maybe this is was OP wants, well I think it is ;) >>>>>> 529/29 > irb >>>>>> irb(main):001:0> a='a b c d e f' >>>>>> => "a b c d e f" >>>>>> irb(main):002:0> a.gsub!(" ","") >>>>> Nope, he wants a method that only replaces the first n occurrences of >>>>> the pattern. gsub will not do this - you need to run sub in a loop. >>>>> (This is where perl's "continue matching where you left off" would >>>>> have been a nice optimisation) >>> The primary problem is that sub! returns nil when no substitutions are >>> made. >>> I have similar problem with Array#flatten!, Array#uniq! since it causes >>> problem when chaining! >>> My solution has been to override these bang! methods to return self even >>> when the bang! method did not have to change the receiver. >>> for sub! I would override the sub! method as follow >>> class String >>> alias old_sub! sub! >>> def sub!(pattern, replacement) >>> self.old_sub!(pattern, replacement) >>> self >>> end >>> end >> And this can easily be expanded to do what I was trying to do in the >> first place! Adding the following method with your replacement method: >> >> class String >> def subn!(pattern, replacement, n=1) >> n.times do >> self.sub!(pattern, replacement) >> end >> end >> end >> >> With this I can specify a.subn!(' ', '', 10) and it will return 'abcdef' >> just like I was looking for. It will work with zero or negative values >> returning 'a b c d e f', again like I would expect. Of course if you >> enter a non-integer value it will error out in its current state but the >> error is from the times method. >> >>> As a user of a method, I really do not care if the method did not have >>> to do anything so long as the object is in the desired state. >> I too hate surprises. >> >> >> >>> For an array such as arr = [1,2,3,4,5,6], I can safely chain the bang! >>> methods without worrying about nil returns from flatten! and uniq! >>> arr.flatten!.uniq!.sort! # NO ERROR MSG "undefined method `uniq!' for >>> nil:NilClass (NoMethodError)" because flatten! would have returned nil >>> p arr => [1, 2, 3, 4, 5, 6]- Hide quoted text - >> - Show quoted text -- Hide quoted text - >> >> - Show quoted text - > > Note that as written, subn! returns n. So if you need to do some > chaining, you should change subn! as follows: > > def subn!(pattern, replacement, n=1) > n.times { self.sub!(pattern, replacement) } > self > end > > str = "a b c d e f g h i j" > str.subn!(/ /,'',2).reverse! # won't produce an error msg such as > > C:/Documents ... /rb5C.tmp:30: undefined method `reverse!' for > 1:Fixnum (NoMethodError) > > instead of => "j i h g f e d cba" > I made your suggested change with one cosmetic change. I added return in front of the self to remind me what is the result of the method. Thanks for the improvement.