http://www.perlmonks.org?node_id=343775

Plankton has asked for the wisdom of the Perl Monks concerning the following question:

Friends,

I want to replace all characters ( a-zA-Z ) with another randomly selected character. Here's what I have tried ...
$ cat text_ob.pl #!/usr/bin/perl -w use strict; my $randChar = sub () { return chr(int(rand(26)) + 65); }; while (<DATA>) { #s/(\w)/(??{$randChar->()}/g; s/(\w)/$randChar/g; print $_; } __DATA__ ABCDEFG $ ./text_ob.pl CODE(0x1014078c)CODE(0x1014078c)CODE(0x1014078c)CODE(0x1014078c)CODE(0 +x1014078c)CODE(0x1014078c)CODE(0x1014078c)
... this was not what I wanted :( I was hopeing for output that looked like this ...
HJAxQPv
Am I on the right track or way off?

Plankton: 1% Evil, 99% Hot Gas.

Replies are listed 'Best First'.
Re: s/\w/random character/g
by Abigail-II (Bishop) on Apr 08, 2004 at 22:18 UTC
    Using Dominus 'identity' trick:
    #!/usr/bin/perl use strict; use warnings; no warnings qw /syntax/; tie my %h => 'main'; sub TIEHASH {bless []} sub FETCH {chr (ord ('A') + int rand 26)} while (<DATA>) { s/(\w)/$h{$1}/g; print; } __DATA__ ABCDEF
    Abigail
      Neat little trick. I didn't realize you could tie entities to package main, though I don't see why not, and your demonstration shows there's nothing preventing it from being done.

      The  no warnings qw/syntax/; line seems unnecessary. Is there a reason for it?

      I wasn't able to find anything that looked particularly questionable about the code, and running with full warnings it didn't produce any complaints. I'm running Perl version 5.8.2 for Win32. Is there an issue with warnings under other versions? ...just curious.

      Also, out of curiosity, what advantage is there to implementing a tied hash in this situation as opposed to a tied scalar?

      Update: Trying to answer my own question I did re-implement your solution with simple tied scalars, and found no functional difference, so I assume that the choice to tie a hash was either habbit, or based on something that hasn't occurred to me.

      Again, loved the trick.


      Dave

        The no warnings qw/syntax/; line seems unnecessary. Is there a reason for it?
        For this program, that line doesn't serve any purpose. But then, neither do the two lines above - yet you aren't inquiring about them. The first six lines are generated whenever I type ^A-P (two chars), and I don't bother to remove the last line if it happens to occur in a program where it's unnecessary.

        Trying to answer my own question I did re-implement your solution with simple tied scalars, and found no functional difference,
        The fact that the entire word is replaced with identical characters didn't strike you as a functional difference? In this specific case, you could get away with using tied scalars if you also used /e, but if letters had to be replaced with a random letter enclosed in parenthesis, using a tied has would lead to:
        s/(\w)/($h{$1})/g;
        while you would need something like:
        s/(\w)/"($s)"/eg;
        which, while it has less characters, is less obvious, IMO.

        Abigail

Re: s/\w/random character/g
by esskar (Deacon) on Apr 08, 2004 at 22:13 UTC
    #!/usr/bin/perl -w use strict; sub randchar { return chr int(rand(26)) + 65; } while (<DATA>) { s!\w!&randchar!ge; print $_; } __DATA__ ABCDEFG
Re: s/\w/random character/g
by saintmike (Vicar) on Apr 08, 2004 at 22:17 UTC
    First off, if you want the replacement part of a substitution operation interpreted as perl code, use the /e switch. Second, if you have a reference to a subroutine, use $ref->() to call it.

    Third, don't use integer numbers for ASCII characters -- just use the characters instead.

    Check this out:

    use strict; use warnings; my @CHARS = ('A'..'Z', 'a'..'z'); sub rand_char { return $CHARS[rand @CHARS]; } while (<DATA>) { s/\w/rand_char()/eg; print $_; } __DATA__ ABCDEFG
Re: s/\w/random character/g
by Not_a_Number (Prior) on Apr 08, 2004 at 23:04 UTC
    print int rand 2 ? chr rand(26) + 97 : chr rand(26) + 65 for split //, <DATA>

    dave

Re: s/\w/random character/g
by belg4mit (Prior) on Apr 08, 2004 at 23:14 UTC
    Ignoring whitespace and symbols (which you seem to be) wouldn;t it be easier to just create a random string to start off with?

    --
    I'm not belgian but I play one on TV.

      Hi belg4mit,

      Actually I do care about white space and etc though you can't tell that from my original post. With the help I got from you helpful monks here's what I ended up with ...
      $ cat text_ob.pl #!/usr/bin/perl -w use strict; sub randchar { return chr int(rand(26)) + 65; } sub randchar2 { my $t = shift; return chr int(rand(26)) + 65 if $t =~ /[a-zA-Z]/; return int(rand(9)) if $t =~ /\d/; return $t; } while (<>) { s/(.)/&randchar2($1)/ge; print $_; } $ ./text_ob.pl < text_ob.pl #!/VYX/ZEE/AOIV -M MVV AMGMXL; YHN DTRQSXDS { GJAHJL MMY BMO(OLTR(18)) + 70; } FZJ EVXHJEPY7 { KP $U = RLOFG; VCDZBZ VOD GAU(GHOE(70)) + 67 ZF $F =~ /[E-V|D-K]/; RPYZOA ADX(ERSW(1)) EM $E =~ /\R/; GDQIZW $W; } VKSXA (<>) { F/(.)/&XDGFXIBL8($8)/QW; DTMTE $_; }