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

The subroutine rand_elt chooses and return a random element of the parameter list.

sub rand_elt { $_[rand @_] }
How it works? Now choosing a random key from the hash is easy.

my $prefix = rand_elt keys %hash;

Replies are listed 'Best First'.
Re: How it works?
by toolic (Bishop) on Jan 20, 2016 at 19:15 UTC
    Assume your hash has 15 keys. @_ is a list of all the keys. As an EXPR for rand, @_ is evaluated in scalar context, which results in the number of items in the array (15). rand @_ is like rand 15, which returns an integer between 0 and 14. Say it returns 6. Then, the 7th key ($_[6]) is returned by the sub.

    UPDATE: I agree with the reply below that rand does not return an integer.

      "rand @_ is like rand 15, which returns an integer between 0 and 14."

      Actually, that's incorrect (with respect to rand returning an integer). The first sentence of the rand documentation reads:

      "Returns a random fractional number greater than or equal to 0 and less than the value of EXPR."

      As an example (which I'll continue to use below):

      $ perl -le '$x = rand 2; print $x' 0.0874574767967786

      I believe, although I can't find any documentation to back it up, that Perl knows the index must be an integer and applies a behind-the-scenes int (or equivalent) to the index. I can use the above rand result directly without getting a warning:

      $ perl -wle '@y = qw{a b c}; print $y[0.0874574767967786]' a

      Even B::Deparse only reports 0 as the index:

      $ perl -MO=Deparse -e '@y = qw{a b c}; print $y[0.0874574767967786]' @y = ('a', 'b', 'c'); print $y[0]; -e syntax OK

      I'd be interested if anyone has more information about this (including, but not limited to, if this behaviour is documented).

      — Ken

        Implicit int-ing also works at run-time:

        c:\@Work\Perl\monks>perl -wMstrict -le "my @ra = qw(e h l o); my ($i, $j, $k, $l) = (0.99876, 1.00123, 2.499999, 3.5); print $ra[$j], $ra[$i], $ra[$k], $ra[$k], $ra[$l]; " hello c:\@Work\Perl\monks>perl -wMstrict -MO=Deparse,-p -le "my @ra = qw(e h l o); my ($i, $j, $k, $l) = (0.99876, 1.00123, 2.499999, 3.5); print $ra[$j], $ra[$i], $ra[$k], $ra[$k], $ra[$l]; " BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } use strict 'refs'; (my(@ra) = ('e', 'h', 'l', 'o')); (my($i, $j, $k, $l) = (0.99876, 1.00123, 2.499999, 3.5)); print($ra[$j], $ra[$i], $ra[$k], $ra[$k], $ra[$l]); -e syntax OK
        Dunno about the documentation.


        Give a man a fish:  <%-{-{-{-<

      Thanks!
      Thanks!
Re: How it works?
by shmem (Chancellor) on Jan 20, 2016 at 22:57 UTC

    This

    sub rand_elt { $_[rand @_] }

    is a terse version of that:

    sub rand_elt { my @keys = @_; my $num_keys = scalar @keys; my $random_index = int( rand($num_keys) ); return $keys[$random_index]; }

    In my experience, writing a nifty condensed sub like the one below "This" will gain you the questionable reputation of writing "write-only code" at your company or elsewhere, among those which aren't familiar neither with the concepts of perl nor with the language in general. Best is to endure such as camels do - with some sort of सच्चिदानन्द at the beast level for starters, or higher.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

      Your explanation is perfect—aces—but I assert that $array[rand@array] is a perfectly cromulent Perl idiom and if we are writing our Perl code for Pythonistas, PHPeepers, Javatards, and Ruby fanbois… That sucks the love of being JAPH right out.

        Assertion approved. I've been working at a shop where coding standard demanded me to write perl like shell, and worse. No (key => $value) x !! $flag in hash constructors, no statement modifiers, no nested expressions like $array[rand@array] and else. Never, ever, unless. Yuck.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'