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

Suppose I have the following hashs -
%verbs = {"jumped","eat","killed"}; %pronoun = {"I","You","He","She"};
How would I go about producing the following output -
He jumped She killed I eat
using code something like this -
foreach my $verb (shuffle keys %verb) { print $RANDOM_PRONOUN.' '.$verb.';' }
I otherwords, what should I use in place of $RANDOM_PRONOUN
Thanks.

Replies are listed 'Best First'.
Re: Select randomly from a hash
by Zaxo (Archbishop) on Oct 16, 2004 at 22:43 UTC

    Your data structures look more like arrays. The standard idiom for a random selection from an array is $array[rand @array]. If you want a shuffle, there is one in List::Util.

    After Compline,
    Zaxo

Re: Select randomly from a hash
by pg (Canon) on Oct 16, 2004 at 22:41 UTC

    Syntax error. I think you wanted @verbs, and @pronouns, not hash.

    You can do something like:

    use warnings; use strict; my @verbs = ("jumped","eat","killed"); my @pronoun = ("I","You","He","She"); while (1) { print $pronoun[rand @pronoun] . " " . $verbs[rand @verbs], "\n"; sleep(1); }
Re: Select randomly from a hash
by Enlil (Parson) on Oct 16, 2004 at 22:44 UTC
    Hashes require an even amount of elements. The first of each pair is the key and the second is that key's value. So warnings would tell you your %verbs hash contains an odd amount of elements. Take a look at perldata. I think what you are looking for are simple arrays, and getting random combinations of two arrays via rand. Something like this should work:
    use strict; use warnings; my @pronouns = qw/He She I Me/; my @verbs = qw /jumped ate laughed cheated/; print $pronouns[rand @pronouns] . ' ' . $verbs[rand @verbs] . $/;

    -enlil

Re: Select randomly from a hash
by davido (Cardinal) on Oct 17, 2004 at 04:02 UTC

    The fact that it looks more like you should be using an array notwithstanding, I'll go ahead and answer the question asked in the subject line anyway:

    Here is one way to select a single random element from a hash:

    my $randelement = ( keys %hash )[ rand( scalar keys %hash ) ];

    This works by creating a list of the keys, and then by generating a random number between zero and n, where n is the number of keys. Using keys in scalar context is an efficient way of determing the number of keys in the hash. That random number is then used as a list index (like an array index) to grab the element corresponding with that random index number. The fact that rand generates floating point numbers doesn't matter; array and list indices are implicitly truncated to integers. That also means that 10.999 will be the element number 10, which is the 11th element. And 0.999 will be element number 0, the 1st element.

    If you're going to be grabbing several elements, and don't want to repeat, use a Fisher Yates shuffle, from List::Utils and then iterate over the list.


    Dave

Re: Select randomly from a hash
by steves (Curate) on Oct 16, 2004 at 22:43 UTC

    What's wrong with:

    %verbs = {"jumped","eat","killed"}; %pronouns = {"I","You","He","She"}; my @pronouns = shuffle keys %pronouns; foreach my $verb (shuffle keys %verbs) { print shift(@pronouns), ' ', $verb, "\n"; @pronouns = shuffle keys %pronouns if (scalar(@pronouns) == 0); }

      Well, for one thing { } is the anonymous hashref constructor and you'd get an odd-number-of-elements-in-hash-assignement warning. And for another you left off use List::Util qw( shuffle ).

      The keys of %verbs are "jumped" and "killed". The values associated with those keys are "eat" and undef.

      The keys of %pronouns are "I", and "He". The values associated with those keys are "You" and "She".

      So you're shuffling two pronouns, not four, and two verbs, not four. So the only possible pronouns, given your code, will be "I" and "He". "She" and "You" will never be selected. Bummer for you and her. ;)

      And with your code (assuming you fixed the missing List::Utils, and uneven key/value pairs), the only actions that He and I could perform would be jumped and killed. ...we would never eat.


      Dave