bbiker
7/23/2007 6:46:00 PM
On Jul 23, 11:51 am, bbiker <renard3...@gmail.com> wrote:
> On Jul 22, 10:39 pm, "Michael W. Ryder" <_mwry...@worldnet.att.net>
> wrote:
>
>
>
>
>
> > dbl...@wobblini.net wrote:
> > > Hi --
>
> > > On Sun, 22 Jul 2007, Bernard Kenik wrote:
>
> > >> ruby-talk-ad...@ruby-lang.org wrote:
>
> > >>> ------------------------------------------------------------------------
>
> > >>> Subject:
> > >>> Re: Is there a replacement for sub?
> > >>> From:
> > >>> "Robert Dober" <robert.do...@gmail.com>
> > >>> 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 <martindeme...@gmail.com> wrote:
> > >>>> On 7/20/07, Robert Dober <robert.do...@gmail.com> 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
>
> > >> 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.
>
> > >> 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]
>
> > > I would very, very strongly advise you, and everyone else, not to do
> > > this. You will break any code (inside the standard library and/or any
> > > other code you load, or any code that uses your code) that depends on
> > > the documented behavior of these methods. You may not like how sub!
> > > and friends work, but it's a very bad idea to make the decision to
> > > change them on behalf of everyone else, over and above the language
> > > documentation.
>
> > What I planned to do was get rid of the alias and change the name of the
> > new sub! method to something like subf! for just that reason. I still
> > prefer this version much better than the original version as there are
> > no surprises. It is very hard to change 30+ years of practice overnight.
>
> > > David- Hide quoted text -
>
> > - Show quoted text -- Hide quoted text -
>
> > - Show quoted text -
>
> Actually you do not have to define subf! as an intermediate step
> you can directly define subn and subn! directly
>
> class String
> def subn!(pattern, replacement, n = 1)
> n.times { self.sub!(pattern, replacement) }
> self # or return self
> end
>
> def subn(pattern, replacement, n = 1)
> return self if n < 1
> @str = self.sub(pattern, replacement)
> (n-1).times { @str = @str.sub(pattern, replacement) }
> @str # or return @str
> end
> end
>
> Note that sub and sub! are the original definitions
>
> You use subn(pattern, replacement) and subn!(pattern, replacement) as
> substitutes for sub(pattern, replacement) and sub!(pattern,
> replacement) which eliminate the unpleasant surprises.
>
> In addition, both can be safely chained !!!!
>
> Hope this helps- Hide quoted text -
>
> - Show quoted text -
You might consider adding the following, so you have a matching
replacement set for sub, sub!, gsub, and gsub!
You can think of gsubn and gsubn! as no nil returns :>)
class String
def subn!(pattern, replacement, n = 1)
n.times { self.sub!(pattern, replacement) }
self
end
def subn(pattern, replacement, n = 1)
return self if n < 1
@str = self.sub(pattern, replacement)
(n-1).times { @str = @str.sub(pattern, replacement) }
@str
end
def gsubn!(pattern, replacement)
self.gsub!(pattern, replacement)
self # does not return nil if no
changes were made
end
alias gsubn gsub # you can use either gsubn or gsub
end
My primary objection to have a nil return is that it prevents me from
safely chaining bang! methods.
The nil return is counter-intuitive and violates the Principle of
Least Surprise
As I said before given an array such as arr = [1, 2, 3, 4, 5, 6], I
can do
new_arr = arr.flatten.uniq.sort => [1, 2, 3, 4, 5, 6]
Intuitively I would think that I should be able to do
arr.flatten!.uniq!.sort!; however because of the nil return by
#flatten!, a NoMethodError is raised by #uniq! since the nil object
does not have a uniq! method.
Note that not all bang! methods return nil when nothing was changed in
the receiver. Array#sort! return self if self was already sorted.
Hopefully Matz might be reading this thread and might consider
changing the behavior of bang! methods returns.