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

Is the following a reasonable procedure to randomly display all the contents of an array without repetition of the array's contents?


#!/usr/bin/perl -w use strict; my @chars = ( 'A' .. 'Z' ); for (0 .. $#chars){ my $randomIndex = rand( @chars ); print "\n$chars[$randomIndex]"; splice( @chars, $randomIndex, 1 ); } exit;



Chris

Replies are listed 'Best First'.
Re: Random but non-repeating array loop
by japhy (Canon) on Jan 18, 2002 at 21:16 UTC
    You could shuffle the array (using the Fisher-Yates method from the FAQ) and then loop over it.

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      ...which has been also been implemented by Abigail's Algorithm::Numerical::Shuffle module. eg:
      use Algorithm::Numerical::Shuffle; foreach my $element (shuffle @array) { ... }

      -----------------------------------------------------
      Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      "I can see my house from here!"
      It's not what you know, but knowing how to find it if you don't know that's important

Re: Random but non-repeating array loop
by I0 (Priest) on Jan 18, 2002 at 21:21 UTC
    for (0 .. $#chars){ my $randomIndex = rand( @chars ); print "\n$chars[$randomIndex]"; $chars[$randomIndex] = $chars[-1]; pop @chars; }
Re: Random but non-repeating array loop
by sifukurt (Hermit) on Jan 18, 2002 at 23:05 UTC
    As japhy suggests, the Fisher-Yates shuffle is a good solution. If you want to save yourself the trouble of coding it, I've got it implemented in Math::NumberCruncher. Math::NumberCruncher also has numerous other similar functions that may be useful. Using Math::NumberCruncher, you could do the above like so:
    use strict; use Math::NumberCruncher; my @chars = ( 'A' .. 'Z' ); my $ref = Math::NumberCruncher->new(); $ref->ShuffleArray( \@chars ); foreach my $char ( @chars ) { print $char, "\n"; }

    UPDATE: Pursuant to Juerd's post, in the interest of TMTOWTDI, here's the code using Math::NumberCruncher through its original functional interface:
    use strict; use Math::NumberCruncher; my @chars = ( 'A' .. 'Z' ); Math::NumberCruncher::ShuffleArray( \@chars ); foreach my $char ( @chars ) { print $char, "\n"; }


    ___________________
    Kurt
      Nice module, but why can it do OO? It seems to me that there's no persistent data, so an object isn't useful. Am I wrong here?

      2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

        Thanks. And you're absolutely right...there isn't a need for it to do OO. You don't have to use it that way; it still supports use as functions. In the first versions, it didn't have an OO interface, but I added it as an option for the sake of being complete, and also to open the door for the use of persistent data in future versions. It is also handy as shorthand if you're making repeated calls to numerous functions within Math::NumberCruncher.
        ___________________
        Kurt
Re: Random but non-repeating array loop
by YuckFoo (Abbot) on Jan 19, 2002 at 00:35 UTC
    Seems perfectly reasonable to me. Here's a similar but shorter construction:

    while ($ch = splice(@chars, rand(@chars), 1)) { print "$ch"; }

    YuckFoo

      Nice, but what happens if @chars happens to contain the element '0'? To avoid having the data potentially short-circuit your loop, I might use something like:
      while ($ch = splice(@chars, rand(@chars), 1),@chars) { print "$ch"; }
      Which will exit the loop when @chars is empty, instead of when splice returns FALSE....

      -Blake

        Except that returns false BEFORE @chars is actually empty.
        One element short.
        You should use:
        while (($ch = splice(@chars, rand(@chars), 1))) { print "$ch"; }
        Or wherever you want to place the parentheses.
        Doh! Thanks for the pointer. Seems this will also fix '0' data:

        while (($ch) = splice(@chars, rand(@chars), 1)) { print "$ch"; }

        YuckFoo

Re: Random but non-repeating array loop
by Stegalex (Chaplain) on Jan 18, 2002 at 22:04 UTC
    If this is for the purposes of debugging, I believe you could also just use Data::Dumper. I like chicken.