[Wellington-pm] Select at random from a list
Douglas Bagnall
douglas at paradise.net.nz
Mon Jul 17 05:26:11 PDT 2006
Grant McLean wrote:
> Perhaps it would work better if you maintained some state between
> selections. For example, you could start by copying the contents of
> playlist.txt to a temporary file (writable by the server process), then
> each request would:
>
> 1. slurp all the entries from the temporary file into an array
> 2. make a random selection and remove that one from the array
> 3. write all the remaining items back to the temporary file
>
> At step 1, if the temporary file was found to be empty then the input
> could be taken from the original playlist file.
You could also do this with a closure. Although my perl is rusty and
probably suspect, the following works:
------------------------------
use strict;
use warnings;
sub shuffle{
my @all = @_;
my @left;
return sub {
while (1){
return splice(@left, rand(@left), 1) if @left;
@left = @all;
}
}
}
my @playlist = qw{a b c d};
my $playlist = shuffle(@playlist);
for (1..12){
print &$playlist()." ";
}
------------------------------
It will print something like:
a c d b d c b a d b a c
The effect is semantically equivalent to Grant's solution, but saves
state in the shuffle function rather than a temporary file.
The subroutine returned by shuffle() is stuck in shuffle's namespace, so
it sees @left and @all even when it is called from outside (just like a
normal sub can see top-level variables).
@all and @left are initialised to the input list, but each time the
returned sub is called, @left is depleted by one item. It refills when
empty, and the cycle continues.
This is still imperfect, however, if you don't want back-to-back
repeats. If the list is shuffled first as "a c b d", then as "d c a b",
song d repeats.
so perhaps something like this would be better:
--------------------------------------
my $WAIT = 1;
sub shuffle2{
my @mp3s = @_;
return sub {
my $next = splice(@mp3s, rand(@mp3s - $WAIT), 1);
push (@mp3s, $next);
return $next;
}
}
my @playlist = qw{a b c d};
my $playlist = shuffle2(@playlist);
my ($a, $b) = ('', '');
my %count;
for (1..1000){
$a = &$playlist();
die "repeating!!" if ($a eq $b);
#print $a;
$b = $a;
$count{$a}++;
}
while (my ($k, $v) = each %count) {
print "$k: $v\n";
}
---------------------------
$WAIT determines how many intervening songs are necessary before a
repeat. Obviously it can't be less than 1 or greater than @mp3s - 1,
and would probably be best determined dynamically.
regards,
douglas
More information about the Wellington-pm
mailing list