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

Over on StackExchange I posted an answer to a question that takes an input file and replaces each digit 0-9 with a random lowercase letter a-z - a different set of random letters on each run of the program, but within one run of the program, each digit should always be replaced by the same letter. The following is what I came up with, but I had a feeling this could be golfed, because the whole shuffle invocation adds a lot of characters. (One idea I had was maybe the random order of hash keys could help, but I guess I'm not feeling creative enough.) Any ideas? :-)

perl -MList::Util=shuffle -M5';$x=join"",shuffle a..z' -pe 'eval"tr/0-9/$x/"' input.txt

WebPerl link (requires modern browser)

Update: Thanks everyone for the inspiring responses so far! :-)

Replies are listed 'Best First'.
Re: A little golfing challenge: replace digits by random letters
by BrowserUk (Patriarch) on Feb 10, 2019 at 11:24 UTC

    Not a perfect shuffle, but close (enough?) and much shorter:

    perl -M5';$x=join"",sort{rand()<rand}a..z' -pe 'eval"tr/0-9/$x/"' input.txt

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit

      half-blind-sort is a great idea (and so is {rand()<rand} ).

      Couldn't resist sort{rand(2)-1} as it's shorter by 2 and truer blind.

      Update/Correction: as vr rightly notes, int(rand(2)-1) is constantly 0!!! Because sort expects an integer back from the comparison BLOCK and int()'ing rand(2)-1 leaves just 0 :(. So, both these compensate for that bug and are uniformly random too: sort{rand(6)-4} and sort{1-rand(3)%3}

        Perhaps sort{rand(2)-1} (in effect, same as sort{0}) is somewhat too deterministic :). But

        $ perl -E 'say sort{rand 2}a..z'

        produces random enough strings, and is shorter.

Re: A little golfing challenge: replace digits by random letters (updated) -- oneliner
by Discipulus (Canon) on Feb 10, 2019 at 13:49 UTC
    Hello haukex,

    have you decided to consume all our spare times!?! ;)

    Not golfed but maybe of interest

    perl '-M0;map{$h{$_}++}97..122;@a=map{chr$_}keys%h;$"=""' -pe 'eval"tr +/0-9/@a[0..9]/"' input.txt
    WebPerl link (requires haukex ;)

    L*

    UPDATE see it running

    perl '-M0;$h{$_}++for 97..122;$"=""' -pe 's/(\d)/(map{chr$_}keys%h)[$1]/eg' input.txt

    removing unneeded assignemnts..

    perl '-M0;$h{$_}++for 97..122' -pe 's/(\d)/(map{chr$_}keys%h)[$1]/eg' input.txt

    or even..

    perl '-M0;@h{97..122}=1x125' -pe 's/(\d)/(map{chr$_}keys%h)[$1]/eg' input.txt

    ...

    perl '-M0;@h{97..122}=1x125'-pe 's/\d/(map{chr$_}keys%h)[$&]/eg' input.txt

    ...

    perl '-M0;@h{97..122}=1x125'-pe 's/\d/chr((keys%h)[$&])/eg' input.txt

    ...

    perl '-M0;undef@h{97..122}'-pe 's/\d/chr((keys%h)[$&])/eg' input.txt

    ..stop!

    perl '-M0;@h{97..122}=1'-pe 's/\d/chr((keys%h)[$&])/eg' input.txt

    L*

    congrats Eily for the below reduction!

    another variation, not highly random..

    perl -pe 's/\d/(a..z)[$&-(split"",time)[-1]]/eg' input.txt

    or.. (shorter than Eily's one ;)

    perl -pe 's|\d|(a..z)[$&-time%7]|eg' input.txt

    after haukex's suggestion:

    perl -pe 's|\d|(a..z)[$&-$^T%7]|eg' input.txt

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

      That's already impressive, but why bother with chr in the first place? perl '-M0;@h{+a..z}=1'-pe 's/\d/(keys%h)[$&]/eg' input.txt works just as fine.

      Edit: this can still be reduced a little:

      perl '-M0;@h{a..z}=1'-pe 's/\d/(%h)[$&*2]/eg' input.txt

      or.. (shorter than Eily's one ;)

      perl -pe 's|\d|(a..z)[$&-time%7]|eg' input.txt
      That's nearly always correct (if you ignore the fact that it's a shift, not a shuffle). You'll get different values if the lines are not treated in the same second. This would happen if the first line is handled at 9:59:59.9999 and the second at 10:00:00.0000 (add the proper number of digits). Or if you are working on a piped input from a slow program, or the input is big enough. This can be demonstrated by adding a sleep.

      I realized I could shorten the other version (I don't feel like calling it mine when I just took your idea and rewrote it :P), but it's still longer than your latest proposition:

      perl -pe '@h{a..z}=1;s/\d/(%h)[$&*2]/eg' input.txt

      Because since this is golfing, why would you bother doing things properly/efficiently? :P

        You'll get different values if the lines are not treated in the same second.

        I had already /msg'd Discipulus about this - the time can be replaced by $^T :-)

Re: A little golfing challenge: replace digits by random letters
by bliako (Abbot) on Feb 10, 2019 at 17:16 UTC

    rand knows all!

    echo 123412341234 | perl -pe '$s=time;s/(.)/srand $s;rand for 1..$1;ch +r(ord("a")+rand 26)/ge'

    bw, bliako

    Edit: fixed the 'a' to "a" also a warning: this code should not be run more than once during one whole second, thanks Discipulus

    Edit: Of course that warning is because of srand(time). Now this seeds and on something completely different: echo '01234567890123456789' | perl -pe '$s=time+join "",{}=~/(\d+)/g;s/(.)/srand $s;rand for 0..$1;chr(ord("a")+rand 26)/ge'. (👁️)

      Hello bliako,

      apart the typo ( 'a' insted of "a") I see duplicates in you code output and more characters than expected

      See it here

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

        Hello there Discipulus, grazie for the alerts and for fixing the quotes issue serving me my own medicine.

        The duplicates are because you ran it too fast! You need to run this code once a second or slower. The universe needs some time to settle ... i.e. the RNG seeds on time whose granularity is the second.

        However, here is a version which seeds on something different:

        echo '01234567890123456789' | perl -pe '$s=time+join "",{}=~/(\d+)/g;s +/(.)/srand $s;rand for 0..$1;chr(ord("a")+rand 26)/ge'

        More characters than expected I have not observed.

        This test may be useful for others too:

        echo '01234567890123456789' | perl -MTest::More -pe ' my $inp = $_; my $L = length($inp); my $N = 100; # number of trials $nt=0; my %previous = (); for(1..$N){ my $tmp = $inp; my $s = join "$_", {} =~ /(\d+)/g; # because ... $tmp =~ s/(.)/srand $s;rand for 0..$1;chr(ord("a")+rand 26)/ge +; $previous{$tmp} = 1; ok(length($tmp)==$L, "length of conversion (".length($tmp).") +equals expected ($L)."); $nt++; } ok(scalar(keys %previous)==$N, ($N-scalar(keys %previous))." duplicate +s among $N trials"); $nt++; done_testing($nt); '