Is there a replacement for sub?

I was trying to come up with a way to remove x instances of a character
from a string and came up with a problem. If I enter:

a = “a b c d e f”
for i in 1…3
a = a.sub!(’ ', ‘’)
end
puts a ==> returns ‘abcd e f’ which is correct.

But if I enter:

a = “a b c d e f”
for i in 1…10
a = a.sub!(’ ‘, ‘’)
end
puts a ==> returns error.rb:3: private method `sub!’ called for
nil:NilClass (NoMethodError, and a is now nil.

What I am looking for is a way to remove the first n instances of a
blank from the string without wiping out the string if it does not
contain at least n blanks. I assume there is a way to do this with
regular expressions, but have not found it yet. This is something an
editor I liked, UCEDIT, on the CDC Cyber had in the 70’s.

On Jul 19, 8:59 pm, “Michael W. Ryder” [email protected]
wrote:

regular expressions, but have not found it yet. This is something an
editor I liked, UCEDIT, on the CDC Cyber had in the 70’s.

sub! modifies the string in place, so you don’t need to say a = a.sub!
(’ ‘,’'). a is already changing. And since sub! is modifying in
place, it returns nil if no changes are being made, and you end up
setting a to nil when that happens.

a = ‘a b c d e f’
10.times { a.sub!(’ ‘,’')}
puts a # ‘abcdef’

HTH,
Chris

Michael W. Ryder wrote:

a = “a b c d e f”
for i in 1…10
a = a.sub!(’ ‘, ‘’)
end
puts a ==> returns error.rb:3: private method `sub!’ called for
nil:NilClass (NoMethodError, and a is now nil.

a.sub! returns nil if no matches are found. You want either:

a = a.sub(’ ', ‘’)

or

a.sub!(’ ', ‘’)

And try using times rather than a for loop and a useless variable:

3.times { a.sub!(’ ', ‘’) }

You could also replace the whole thing with:

“a b c d e f”.split(’ ', 4).join

Pete Y.

Chris S. wrote:

blank from the string without wiping out the string if it does not
10.times { a.sub!(’ ‘,’’)}
puts a # ‘abcdef’

HTH,
Chris

I think this is where I am having problems understanding Ruby. I have
to use a.sub(’ ‘, ‘’) in a for loop but use a.sub!(’ ', ‘’) when using a
times loop. Why the difference?

On Jul 19, 2007, at 11:00 PM, Michael W. Ryder wrote:

regular expressions, but have not found it yet. This is something
an editor I liked, UCEDIT, on the CDC Cyber had in the 70’s.

How about this?

n = 3
“a b c d e f”.sub(/(\S\s){#{n}}/) { |m| m.delete(" “) } # => “abcd e f”
n = 10
“a b c d e f”.sub(/(\S\s){#{n}}/) { |m| m.delete(” ") } # => “a b c d
e f”

Regards, Morton

Morton G. wrote:

blank from the string without wiping out the string if it does not

Regards, Morton

Is there nothing in regular expressions where you can tell it to do
something up to n times?

2007/7/20, Michael W. Ryder [email protected]:

puts a ==> returns ‘abcd e f’ which is correct.
What I am looking for is a way to remove the first n instances of a
“a b c d e f”.sub(/(\S\s){#{n}}/) { |m| m.delete(" ") } # => “a b c d e f”

Regards, Morton

Is there nothing in regular expressions where you can tell it to do
something up to n times?

There is - kind of. You can use {} to give repetition counts. You can
do this

irb(main):004:0> a = “a b c d e f”
=> “a b c d e f”
irb(main):005:0> a.sub(/(?: [^ ]*){3}/) {|m| m.gsub(/ /, ‘’) }
=> “abcd e f”
irb(main):006:0>

Kind regards

robert

Hi –

On Fri, 20 Jul 2007, Robert K. wrote:

end

times loop. Why the difference?

There is none. a.sub! is an alternative to a = a.sub - wherever you use it.

a.sub! will modify the same string, though, whereas a = a.sub
changes the binding of ‘a’ to a new string. (Which you probably
didn’t mention because it doesn’t matter in a given case, but could be
misunderstood out of context :slight_smile:

David

2007/7/20, Michael W. Ryder [email protected]:

puts a ==> returns ‘abcd e f’ which is correct.
What I am looking for is a way to remove the first n instances of a
a = ‘a b c d e f’
10.times { a.sub!(’ ‘,’')}
puts a # ‘abcdef’

HTH,
Chris

I think this is where I am having problems understanding Ruby. I have
to use a.sub(’ ‘, ‘’) in a for loop but use a.sub!(’ ', ‘’) when using a
times loop. Why the difference?

There is none. a.sub! is an alternative to a = a.sub - wherever you use
it.

robert

Robert K. wrote:

end

n = 10
do this

Unfortunately this is much more complicated and much harder to
understand, and debug. The editor I mentioned had an argument you
passed to the expression that told it to do it one time if it was
absent, n number of times, or till the end. Since this was 30 years ago
I expected that something like this hadn’t been dropped in the interim.

Robert K. wrote:

a = a.sub!(’ ‘, ‘’)
nil:NilClass (NoMethodError, and a is now nil.
setting a to nil when that happens.
to use a.sub(’ ‘, ‘’) in a for loop but use a.sub!(’ ', ‘’) when using a
times loop. Why the difference?

There is none. a.sub! is an alternative to a = a.sub - wherever you use
it.

Except where you use b = a.sub!(’ ‘, ‘’). Then if you loop through this
statement 10 times b is nil, not the abcdef I expected. This is what is
so confusing. At the same time I can not use b = a.sub(’ ‘, ‘’) as it
always returns ab c d e f regardless of how many times I execute it,
which is what I would expect. So, how would I do this?
10.times { b = a.sub!(’ ‘, ‘’)} doesn’t work, it errors out.
b = a.split(’ ', 10).join works for this contrived example but I am not
sure if it would work for something like replacing the first 12
occurrences of “,”\ with \t.

On 7/20/07, Michael W. Ryder [email protected] wrote:

Except where you use b = a.sub!(’ ‘, ‘’). Then if you loop through this
statement 10 times b is nil, not the abcdef I expected. This is what is
so confusing. At the same time I can not use b = a.sub(’ ‘, ‘’) as it
always returns ab c d e f regardless of how many times I execute it,
which is what I would expect. So, how would I do this?
10.times { b = a.sub!(’ ', ‘’)} doesn’t work, it errors out.

Remember that a variable is not an object, it is just a label pointing
to an object.

sub: returns a copy of the string, with the substitution made (i.e.
a new object)
sub!: modifies the string in place (i.e. modifies the object itself)

Therefore

a = “a b c d e f”
a.sub(" ", “”) # => returns “ab c d e f”, but leaves a unchanged.
also, the return value will be garbage collected, since nothing is
pointing to it

b = a.sub(" ", “”) # => b is now “ab c d e f”, leaves a unchanged

a = a.sub(" ", “”) # => a is now “ab c d e f”, the old string is
garbage collected

a = “a b c d e f”
b = a
a = a.sub(" ", “”) # => a is now “ab c d e f”, b is still “a b c d e f”

a = “a b c d e f”
a.sub!(“”, ‘’) #=> a is now “ab c d e f”

a = “a b c d e f”
b = a.sub!(“”, ‘’) #=> a is now “ab c d e f”, a and b point to the same
object

a = “abcdef”
b = a.sub!(“”, ‘’) #=> a is still “abcdef”, b is nil

Note the last bit carefully - sub! modifies the object, and returns
either another pointer to the same object if it was modified, or nil
if it wasn’t. Therefore the return value of sub! should ideally only
be used to check if the string was modified or not, and then
discarded.

As for running in a loop, you want either

10.times { a.sub!(" ", ‘’)} #=> modifies a string 10 times

or

10.times {a = a.sub(" ", ‘’)} # creates a series of 10 strings, the
intermediate ones being GCd

try this:

a = “a b c d e f”
intermediate = [a]
10.times {|i| intermediate[i+1] = intermediate[i].sub(" ", ‘’)}
p intermediate

=> [“a b c d e f”, “ab c d e f”, “abc d e f”, “abcd e f”, “abcde f”,

“abcdef”, “abcdef”, “abcdef”, “abcdef”, “abcdef”, “abcdef”]

martin

2007/7/20, [email protected] [email protected]:

a = a.sub!(’ ', ‘’)
sub! modifies the string in place, so you don’t need to say a = a.sub!

misunderstood out of context :slight_smile:
Yes. I was just referring to the assumed different usage in for and
while loops (see question). Thanks for clarifying!

Kind regards

robert

From: Michael W. Ryder [mailto:[email protected]]

a = “a b c d e f”

for i in 1…3

a = a.sub!(’ ', ‘’)

^^^^^
lose that

end

puts a ==> returns ‘abcd e f’ which is correct.

But if I enter:

a = “a b c d e f”

for i in 1…10

a = a.sub!(’ ', ‘’)

^^^^^

same. lose it.

end

puts a ==> returns error.rb:3: private method `sub!’ called for

nil:NilClass (NoMethodError, and a is now nil.

a.sub! will modify a. so, do NOT use a=a.sub!, it’s NOT right. just use
plain a.sub!. ruby has already simplified it, do not make it simpler :slight_smile:

eg,

C:\family\ruby>cat -n test.rb
1 puts “—test 3 subs using for—”
2 a = “a b c d e f”
3 for i in 1…3
4 a.sub!(’ ‘, ‘’)
5 end
6 puts a
7
8 puts “—test 10 subs using for—”
9 a = “a b c d e f”
10 for i in 1…10
11 a.sub!(’ ‘, ‘’)
12 end
13 puts a
14
15
16 puts “—test 3 subs using times—”
17 a = “a b c d e f”
18 3.times do
19 a.sub!(’ ‘, ‘’)
20 end
21 puts a
22
23
24 puts “—test 10 subs using times—”
25 a = “a b c d e f”
26 10.times do
27 a.sub!(’ ', ‘’)
28 end
29 puts a

C:\family\ruby>ruby test.rb
—test 3 subs using for—
abcd e f
—test 10 subs using for—
abcdef
—test 3 subs using times—
abcd e f
—test 10 subs using times—
abcdef

C:\family\ruby>

is that clear enough?

kind regards -botp

On 7/20/07, Michael W. Ryder [email protected] wrote:

regular expressions, but have not found it yet. This is something an
editor I liked, UCEDIT, on the CDC Cyber had in the 70’s.

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 :wink:

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!(" “,”")
=> “abcdef”
irb(main):003:0> a
=> “abcdef”
irb(main):004:0>

Consider however using the non inplace version, like e.g.

b = a.gsub(" “,”")

HTH
Robert

On 7/20/07, Robert D. [email protected] 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 :wink:

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)

martin

On 7/20/07, Martin DeMello [email protected] wrote:

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)

I knew I missed something but I did not see what, sorry…

Le 20 juillet à 11:12, Peña, Botp a écrit :

a.sub! will modify a. so, do NOT use a=a.sub!, it’s NOT right.
just use plain a.sub!. ruby has already simplified it, do not make
it simpler :slight_smile:

And, while you’re at it, depending on the situation, sub! returning nil
may be quite useful :

a = “a b c d e f”
for i in 1…1_000
break unless a.sub!(’ ', ‘’)
end

(I.e. breaking if there is nothing else to substitute, instead of
looping 995 times for nothing.)

Fred

2007/7/20, Martin DeMello [email protected]:

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.

There is a different solution that does not need a loop (see one of my
previous posts).

(This is where perl’s “continue matching where you left off” would
have been a nice optimisation)

Well, you can always use the block form with an additional counter

irb(main):012:0> count=0
=> 0
irb(main):013:0> a.gsub(/ /) {count+=1; count<3 ? “” : " "}
=> “abc d e f”

But I guess the other solution is more efficient.

Kind regards

robert

On Fri, 20 Jul 2007 02:59:35 +0000, Michael W. Ryder wrote:

regular expressions, but have not found it yet. This is something an
editor I liked, UCEDIT, on the CDC Cyber had in the 70’s.

a.sub! modifies the string in place, changing only the first occurance.
if it finds something, it returns self, if it finds nothing, it returns
nil (so you can find out whether it changed anything.) So there’s no
need
to do the a=a.sub!. Just use a.sub! on its own.

a.sum (without the!) always returns a string, whether something was
found
or not (in which case it returns the unmodified string), and never
modifies the string itself – it always returns a copy.

gsub! and gsub work similarly, but they change all matching strings in
one fell swoop. (The g in the name is a holdover from standard UNIX
utilities such as sed, ex, and vi that use the letter g as a modifier to
mean “replace all matches”)

–Ken