DavidC has asked for the wisdom of the Perl Monks concerning the following question:
Hey guys,
I have a hash that is going to be used as a letter substitution key: A->B, B->F, C->J, etc. etc.
I have a message that's a scalar.
I'd love to use a regex with the hash keys and values to do this substitution in one or two lines if I can.
The alternative is to ginsu up the scalar into an array, switch each element of the array and then glue it back together into a scalar again.
Neither of these work. :(
# $mes =~ tr/(keys %codewheel)/(values %codewheel)/;
# $mes =~ tr/[keys %codewheel]/[values %codewheel]/;
Edit: Awesome, thanks guys. I recognize the \w and $1, but I didn't know how to include scalars into regex's. Thanks. Um, if we don't use the ()'s in (\w), will it scoop the whole message as one big pattern, or the whole first word or something?
Re: simple letter substitution according to hash
by ptum (Priest) on May 16, 2006 at 14:21 UTC
|
#!/usr/local/bin/perl
use strict;
use warnings;
my %codewheel = ( 'A' => 'B', 'B' => 'C', 'C' => 'D' );
my $message = 'ABC';
$message =~ s/(\w)/${codewheel{$1}}/g;
print "Message: $message\n";
Update: Of course, as pointed out by some of the later responses, this code suppresses any characters that are not accounted-for in your %codewheel hash. For example, with the same translation hash, if your original message was 'ABCXYZ', this code would still translate it as 'BCD' (and produce some warnings). If you don't want that outcome, you'll need to adapt the 'or' (||) logic shown in other responses.
No good deed goes unpunished. -- (attributed to) Oscar Wilde
| [reply] [d/l] |
|
my $message = 'XXX';
# ;-)
| [reply] [d/l] |
Re: simple letter substitution according to hash
by borisz (Canon) on May 16, 2006 at 14:23 UTC
|
$_ = 'Hello World';
my %h = ( e => 'b', l => 'd', );
my $f = join '', keys %h;
my $t = join '', values %h;
eval "y/$f/$t/";
print
__OUTPUT__
Hbddo Wordd
A better choice is to use
$_ = 'Hello World';
my %h = ( e => 'b', l => 'd', );
my $k = join '', keys %h; # Sure, I mean '' Thx@bazar
s/([$k])/$h{$1} || $1/eg;
print
| [reply] [d/l] [select] |
|
$_ = 'Hello World';
my %h = ( e => 'b', l => 'd', );
my $k = join '|', keys %h;
s/([$k])/$h{$1} || $1/eg;
print
I think you want either ($k) or my $k = join '', keys %h; in the latter case one would probably also want to check that the keys are actually single letters. In both cases || $1 wouldn't be necessary any more, since the match would necessarily be one of the keys.
| [reply] [d/l] [select] |
Re: simple letter substitution according to hash
by blazar (Canon) on May 16, 2006 at 14:23 UTC
|
s/([[:alpha:]])/$hash{$1}||$1/ge;
I would also avoid capturing:
s/[[:alpha:]]/$hash{$&}||$&/ge;
but so many people dislike it, so I gave the one with capturing first. Of course tr is generally better suited for this kind of task, as explained in in perldoc perlop, but unfortunately the transliteration table is built at compile time, so you can not ionterpolate data into it. But you can adopt the eval workaround also described in perldoc perlop. | [reply] [d/l] [select] |
|
What do you mean, avoid capturing? Using $& causes all regexps to become capturing regexps. You're not saving anything by using $&, and you end up slowing all non-capturing regexps in that interpreter instance.
| [reply] [d/l] [select] |
|
I meant "capturing parentheses". Or "explicit capturing". Sorry for the imprecision.
You're not saving anything by using $&, and you end up slowing all non-capturing regexps in that interpreter instance.
<rant with="perl" not="you">
You're right, of course. It's just so annoying to have this technical inconvenience. If I match something it is quite probable that I may want to do something with it. Hence the need for a pronoun without the need to say "hey, capture it all for me". Different story for $` and $', obviously.
</rant>
| [reply] [d/l] [select] |
Re: simple letter substitution according to hash
by duff (Parson) on May 16, 2006 at 15:52 UTC
|
$mes.=trans(%codewheel);
(trans is the function name for the tr/// operator in perl6)
| [reply] [d/l] [select] |
Re: simple letter substitution according to hash
by Jasper (Chaplain) on May 16, 2006 at 18:01 UTC
|
s/$key/$value/g while ($key, $value) = each %codewheel;
also works, without any of the nasty joins, or evaluated regexes. Not sure I'd recommend it, though :) | [reply] [d/l] |
|
| [reply] [d/l] [select] |
|
Well spotted. That was stupid of me. I did say I didn't recommend it, didn't I? :)
| [reply] |
Re: simple letter substitution according to hash
by Limbic~Region (Chancellor) on May 17, 2006 at 17:23 UTC
|
DavidC,
I am not sure I understand what the aversion to slicing and dicing is.
$str = join '', map $codewheel{$_}, split //, $str;
If you think that this is terribly inefficient then there is likely more to the story than what you have described. Looking at the whole problem might result in a more desireable solution. Otherwise, avoiding a straight forward approach just because you want to sounds silly.
| [reply] [d/l] |
|
|