Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

simple letter substitution according to hash

by DavidC (Novice)
on May 16, 2006 at 14:15 UTC ( [id://549773]=perlquestion: print w/replies, xml ) Need Help??

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?

Replies are listed 'Best First'.
Re: simple letter substitution according to hash
by ptum (Priest) on May 16, 2006 at 14:21 UTC

    Why not use regex substitution?

    #!/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
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
    Boris
      $_ = '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.

Re: simple letter substitution according to hash
by blazar (Canon) on May 16, 2006 at 14:23 UTC

    IIUC

    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.

      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.

        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>

Re: simple letter substitution according to hash
by duff (Parson) on May 16, 2006 at 15:52 UTC

    In perl6 you'll be able to do this:

    $mes.=trans(%codewheel);
    (trans is the function name for the tr/// operator in perl6)
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 :)
      With the following:
      %codewheel = ( A => 'B', B => 'C', 'C' => 'D' );

      Your code changes 'ABC' --> 'BBC' --> 'CCC' --> 'DDD' and so on.

        Well spotted. That was stupid of me. I did say I didn't recommend it, didn't I? :)
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.

    Cheers - L~R

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://549773]
Approved by Corion
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (3)
As of 2024-04-25 09:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found