I understand the following:
$ ruby -e "p 'abc'.sub('a','A').sub('b', 'B')"
"ABc"
I am OK with the following also:
echo abc | ruby -p -e "sub('a','A');sub('b', 'B')"
ABc
But:
echo abc | ruby -p -e "sub('a','A').sub('b', 'B')"
Abc
I expect the result to be "ABc" as well, why is it not? The second sub('b', 'B') is not operational.
The two cases look similar, but in fact you are running different methods from the Ruby core library in them:
In your first case, i.e. sub('a','A');sub('b', 'B'):
You are running both sub without specifying an explicit receiver, and therefore you are invoking the method Kernel#sub. The Ruby-Doc says about this method:
sub(pattern, replacement)→$_Equivalent to
$_.sub(args), except that$_will be updated if substitution occurs. Available only when -p/-n command line option specified.
The following examples illustrate the differences with and without a receiver:
$ echo abc | ruby -p -e '$_.sub("a","A").sub("b", "B"); $_'
abc
$ echo abc | ruby -p -e 'sub("a","A").sub("b", "B"); $_'
Abc
Hence, in the first example, you really invoke that Kernel#sub twice, and after each invocation, $_ is updated. Therefore, $_ is ABc after the second sub has been executed. At the end of the of the whole expression supplied by -e (i.e. at the end of the implicit loop provided by the -p option), the value of $_ is printed, and you see ABc.
In your second example, i.e.
sub('a','A').sub('b', 'B')
The first sub again is Kernel#sub, as before. It has the effect of turning the string into Abc, and also sets $_ to Abc. However, the second sub now does have an explicit receiver (the string resulting from the first sub), and in this case, the method String#sub is executed. This method produces ABc, but different to Kernel#sub, it does not update $_. Therefore, $_ is still set to Abc, and this is what you see as output.
While it seems to be convenient to rely on the implicit effect certain methods have on $_, it is sometimes easier to make the manipulation explicit. For instance, if you do a
$_=$_.sub('a','A').sub('b','B')
you can clearly see what is going on. An alternative would be
$_.sub!('a','A');$_.sub!('b','B')
Note that in the last case, you do not want to chain the two sub!, because String#sub! returns nil if no substitutions have been performed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With