{$str}<--"; --output:-- -->ABC<-- But with TR///, which creates a new string" /> {$str}<--"; --output:-- -->ABC<-- But with TR///, which creates a new string" /> {$str}<--"; --output:-- -->ABC<-- But with TR///, which creates a new string"/>

TR/// : How do you apply TR to a string?

108 Views Asked by At

With tr///, I can do this:

my $str = 'xABCz';
$str ~~ tr:c/ABC//;

say "-->{$str}<--";

--output:--
-->ABC<--

But with TR///, which creates a new string, there doesn't seem to be a way to apply it directly to a string:

my $str = 'xABCz';
$str ~~ tr:c/ABC//;

my $new_str = $str ~~ TR:c:d/ABC//;
say "-->{$new_str}<--";

--output:--
-->True<--

I can use with to set $_ to $str:

my $str = 'xABCz';

with $str {   # now $_ refers to $str
    my $new_str = TR:c:d/ABC//;  # TR/// is called on $_.
    say "-->{$new_str}<--";
} 

--output:--
-->ABC<--

...or even:

my $str = 'xABCz';
my $new_str = TR:c:d/ABC// with $str;

say "-->{$new_str}<--";

--output:--
-->ABC<--

In perl, I would write:

my $str = 'xABCz';
my $new_str = $str =~ tr/ABC//rcd;  #r - new_string, c - complement, d - delete
say $new_str;

-output:--
ABC
3

There are 3 best solutions below

2
Silvio Mayolo On BEST ANSWER

Str.trans should have the same effect non-destructively.

my $new_str = $str.trans("ABC" => "")
0
codesections On

There is not a valid way to use TR with ~~; TR must always operate on the topic variable (e.g., set with with). TR is the transliteration equivalent of S (nondestructive substitution), and the docs for S note that:

Note: since the result is obtained as a return value, using this operator with the ~~ smartmatch operator is a mistake and will issue a warning. To execute the substitution on a variable that isn't the $_ this operator uses, alias it to $_ with given, with, or any other way. Alternatively, use the .subst method.

Similarly,, using TR with ~~ is a mistake (and, as @raiph noted above, probably should issue a warning). You should instead set $_ using given/etc or use the .trans method.

1
jubilatious1 On

Not sure if I have a 'rule of thumb' about this yet, but one might consider using s/// and tr/// only with the sed-like -pe command-line flags (and/or the ~~ smart-matcher, as the OP has illustrated). For all other cases, use S/// and TR///, setting the topic (e.g. with given) as necessary:

#Below works:

~$ raku -pe 's:g/\,/\t/;' file.csv

#OR

~$ raku -pe 'tr/,/\t/;' file.csv

Where this gets confusing is that sed, as a "stream-editor", picks the value out of the stream, modifies it, and puts ("returns") the value back to the stream for you. Short and sweet--but quite different from awk or practically any other dynamic language you might be playing with.

To accomplish the same task with awk and many other languages, you need an explicit output statement with mutable strings. Here's where the "big-S" and a "big-TR" notations really shine (note the -ne non-autoprinting, awk-like command-line flags):

#Below works:

~$ raku -ne 'S:g/\,/\t/.put;'  file.csv

#OR 

~$ raku -ne 'TR/,/\t/.put;'  file.csv

So this may seem arcane if you're not writing one-liners, but you can simply remember from above: reading lines() from a file will require the "big-S" or "big-TR" notations like in the second (awk-like case), as opposed to the first sed-like case:

#Below with tr/// doesn't work:

~$ raku -e 'for lines() {tr/,/\t/.put};'  file.csv > file.tsv
Cannot modify an immutable Str (input,file,text,here...)
  in block <unit> at -e line 1

#Below with TR/// works as desired:

~$ raku -e 'for lines() {TR/,/\t/.put};'  file.csv > file.tsv

(Finally, you should be aware that Raku did away with -i "in-place" editing of files. I have only anecdotal evidence for the following reasoning, but some SO comments have suggested that Perl end-users were accidentally "emptying" their input files with the wrong combination of command-line flags and "small-s" or "small-tr" operators. Hence the introduction of "big-S" or "big-TR" notations, which are inherently safer).