Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Pick random item from list of unknown length?

by Cody Fendant (Pilgrim)
on Mar 01, 2010 at 03:59 UTC ( #825825=perlquestion: print w/ replies, xml ) Need Help??
Cody Fendant has asked for the wisdom of the Perl Monks concerning the following question:

Semi-idle question -- I have a comma-sparated list of things whose length is unknown, but is at least one:

apple apple,banana,cherry banana,cherry apple,cherry banana cherry

And I have to pick one at random. Obviously if there's just one, we pick that one.

I did it like this:

@sections = split( ',', $_ ); print "CHOSEN: ", $sections[ int( rand(@sections) ) ], "\n";

Is there a way to do it more succinctly without creating that temporary array?

Comment on Pick random item from list of unknown length?
Select or Download Code
Re: Pick random item from list of unknown length?
by Anonymous Monk on Mar 01, 2010 at 04:13 UTC
    Is there a way to do it more succinctly without creating that temporary array?

    Pass a list, not a string ?:)

    $_ = <<'__DERP__'; apple apple,banana,cherry banana,cherry apple,cherry banana cherry __DERP__ print "Say ", ( m!([^,]+)!g )[ scalar rand( tr!,!! ) ], "\n"; __END__ Say banana
      print "Say ", ( m!([^,]+)!g )[ scalar rand( tr!,!! ) ], "\n";

      Counting the commas will give you one less than the numbers of elements in the list so you have to add one to it:

      print "Say ", ( /[^,]+/g )[ rand 1 + tr/,// ], "\n";
        Yup, classic one-off error :) Should also probably count newlines :)
        That works with the implicit regex on $_
        $_ = 'apple,banana,cherry'; print "Say ", (m!([^,]+)!g)[ scalar rand 1 + (tr!,!!) ], "\n";
        but for some reason, when I do this:
        $foo = 'apple,banana,cherry'; print "Say ", ($foo =~ m!([^,]+)!g)[ scalar rand 1 + (tr!,!!) ], "\n";

        I only ever get 'apple'.

Re: Pick random item from list of unknown length?
by ikegami (Pope) on Mar 01, 2010 at 05:54 UTC

    What's the point of your request? Are you trying to void two lines? Easy:

    sub pick_one { $_[rand(@_)] } while (<>) { chomp; print(pick_one(split(/,/)), "\n"); }
Re: Pick random item from list of unknown length?
by JavaFan (Canon) on Mar 01, 2010 at 08:37 UTC
    Is there a way to do it more succinctly without creating that temporary array?
    If you're reading in the file line-by-line, who cares about the extra array?

    But say you're reading from some handle, and you want to randomly pick a line:

    use Algorithm::Numerical::Sample; my $s = Algorithm::Numerical::Sample::Stream->new; while (<>) { $s->data($_): } my $random = $s->extract;
Re: Pick random item from list of unknown length?
by ambrus (Abbot) on Mar 01, 2010 at 11:38 UTC
Re: Pick random item from list of unknown length?
by Anonymous Monk on Mar 01, 2010 at 17:41 UTC
    For any given run of the program, do you only want to draw a single item, or are you wanting to draw items in succession? Are you trying to avoid having the entire list in memory? Do you know anything about the items in your list?

    If you know the average string length of each item (or rather the distance between commas) and the size of the file, you can guess at how many items are in the file. Generate a uniformly distributed random integer, and then process the file until you find the item that you choose. If the file ends before you get there, you could either pick a new number (based upon how many items you now know are there), or start at the beginning (distribution is no longer strictly uniform however).

    If you are going to pick items over and over, you might treat the problem like the interval of time between soldiers being kicked by horses (exponential waiting times, Poisson problems). You generate an exponential deviate and convert to integer. This is how many positions in the list you travel to get to the next item to pick. When you reach end of list, start at the beginning.

Re: Pick random item from list of unknown length?
by ack (Deacon) on Mar 01, 2010 at 20:32 UTC

    I tried the following using an annonymous array and dereference to get a single expression.

    Unfortunatly, since it requres TWO splits (one to generate the list of item in a line and one to figure out how many items are in that line), I don't think it is any more memory efficient.

    In fact, I suspect it is LESS memory efficient since the annonymous arrays (mine uses two whereas the case the OP is trying to get rid o uses on a single array) take up memory.

    But, as others pointed out, the OP wasn't clear on whether the issue was saving space or making the code takes less lines. So this appraoch ONLY saves lines...at the expense, IMHO, of readability and memory.

    #!/usr/bin/perl use strict; use warnings; my @lines = ( "apple", "apple,banana,cherry", "banana,cherry", "apple,cherry", "banana", "cherry", ); foreach my $line (@lines){ my $item = (split(/\,/,$line))[int(rand(scalar(@{[split(/\,/,$line)]})))]; print "$item\n"; } exit(0);
    ack Albuquerque, NM

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://825825]
Approved by ww
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (5)
As of 2014-08-01 02:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (256 votes), past polls