[Wellington-pm] Select at random from a list

Jacinta Richardson jarich at perltraining.com.au
Fri Jul 14 19:27:41 PDT 2006


Philip Plane wrote:
> Hi Guys,
> 
> I have a little script I wrote to return a randomly selected mp3 from a
> playlist. It works, but doesn't seem to be as random as I hoped. Any
> comment on a better way of doing this?

When you say it doesn't seem to be as random as you hoped, what are you 
using as your measurement for randomness?  Normal random playlists 
aren't.  They shuffle the playlist into a random order and then walk 
through that order.   In your code, however, you're selecting a song 
from your playlist at random each time.  Assuming that your machine has 
a decent source of randomness[1], you are just as likely to pick out the 
same song as the previous invocation as you are to pick out any 
other[2].  Although, as these are independent events, the actual odds of 
picking the same song two times in a row may be quite small.

I have only two pieces of feedback on your code:

	$mp3 = $playlist[int(rand(scalar(@playlist)))];

can also be written as:

	$mp3 = $playlist[ rand(@playlist) ];

because both rand expects a scalar argument, and array lookups expect an 
integer; thus the transformations will be done for you if necessary.

Further, you *really* ought to specify a mode whenever you open a file. 
  I know that Perl defaults to reading if you don't specify anything, 
but if you do this in a script where the user has any control over the 
filename, then you open yourself up to all sorts of security problems:

	open PLAYLIST, "< /mp3/playlist.txt" or die ...

(or alternately: 5.6+)
	open PLAYLIST, "<",  "/mp3/playlist.txt" or die ...

The rest looks good.

	J

[1] Perl's rand isn't truly random for multiple calls.  Like many random 
number generators it uses a truly random seed (as far as possible, and 
this is dependent on your operating system), and then a pseudo-random 
number generator for each subsequent call.  Since you're only calling 
rand once during each invocation, this is irrelevant.

[2] If you would prefer to shuffle your songs and then listen to then, 
then you may want to do something like the following.  Code is untested.

#!/usr/bin/perl -w
use CGI;
use List::Util qw/shuffle/;
use strict;

my $curr_playlist = "/tmp/curr_playlist.txt";
my $q = new CGI;

my @playlist;

# Get already shuffled playlist if we have one
if( open CURRPLAYLIST, "<", $curr_playlist ) {
       @playlist = <CURRPLAYLIST>;
       close CURRPLAYLIST;
}

# If we don't have one, or if it's empty, generate one
unless(@playlist) {
      open SONGLIST, "<", "/mp3/playlist.txt" or die "Couldn't open 
playlist: $!\n";
      @playlist = <SONGLIST>;
      close SONGLIST;

      # shuffle song order
      @playlist = shuffle @playlist;
}

# Take a song off the front
my $mp3 = shift @playlist;

# Write current playlist back to file (over-writing existing)
open NEWPLAYLIST, ">", $curr_playlist;
print NEWPLAYLIST @playlist;
close NEWPLAYLIST;

# Generate song
open MP3, "<", $mp3 or die "Couldn't open $mp3: $!\n";
print $q->header(-type=>'audio/mpeg', -Content_length=>(stat MP3)[7]);
print <MP3>;
close MP3;


More information about the Wellington-pm mailing list