Zippie
Zippie

Reputation: 6088

Using variables in a transliteration operator

This is a stupid question; I need this for something more complicated but let's make it simple:

$i = quotemeta 'A';
$line =~ tr/a-z/$i-Z/;

This is a silly substitution, this should turn small letters to capital ones, but what ever I tried doesn't work.

Ok so I see this brought lots of confusion, the real code should be:

$line =~ tr/A-Z/F-ZA-E/;

It should be a Caesers Chiper algorhytm. This works just fine if you want to change it by a fixed number of letters, but I need a variable so I could change it as many number of letters as I want to. But I want to ship eval Thnx

P.S. Also, why doesn't s/// work with ranges?

Upvotes: 4

Views: 869

Answers (4)

TLP
TLP

Reputation: 67910

Note that with tr///, you can only use literal characters, not variables. So, tr/a-z/$i-Z/ does not work, you'd have to use tr/a-z/A-Z/. In perldoc perlop you can read more about tr/// and also find the explanation on interpolation or lack thereof.

To turn a string into upper case, though there are other ways. You can use the uc function, or the regex escape sequences \U (upper case) and \L (lower case). E.g.:

my $str = 'a';
my $uppercase = uc $str;

Regex:

my $str = 'abcdef';
$str =~ s/([a-z]+)/\U$1/;  # $str is now 'ABCDEF'

tchrist points out that [a-z] should be written \p{CWU}, which will include all characters which are affected by case.

Update:

If you're trying to create some kind of dynamic encoding feature, I would suggest a subroutine that takes the arguments, and moves the characters around manually.

sub foo {
    my ($str, $num) = @_;  # num is how many characters to shift the alphabet
    my @in  = 'A' .. 'Z';
    my @out = @in[$num .. $#in, 0 .. $num-1];
    my %out = map { $in[$_] => $out[$_] } 0 .. $#in;
    $str =~ s#(.)# $out{$1} // $1 #eg;  # if key does not exist, use org
    return $str;
}

Note that it is case sensitive, but you can add keys for lower case to solve that.

Upvotes: 4

Mark Reed
Mark Reed

Reputation: 95315

Your example is unfortunately not a good one; the tr operator doesn't accept any variable interpolation (in either half). If you are actually using tr, you are out of luck unless you resort to eval, which is not recommended. If you're not using tr, then please show us the actual code so we can see what's going on and offer more suggestions.

UPDATE

How about this?

my %map;
@map{'A'..'Z'} = ('F'..'Z','A'..'E');
s/[A-Z]/$map{$&}/g;

Not sure if it's still true, but once upon a time $& was to be avoided at all costs due to a terrible performance penalty. You can do so easily enough:

s/([A-Z])/$map{$1}/g;

Upvotes: 2

happydave
happydave

Reputation: 7187

If you really need tr, you could use eval:

$i = quotemeta 'A'
eval("\$line =~ tr/a-z/$i-Z/;");

Upvotes: 4

Kenosis
Kenosis

Reputation: 6204

tr doesn't work with variables; use s/// (substitution) instead.

Upvotes: 0

Related Questions