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

Hi monks,

I've written a script which contains a loop, and i was wondering if i am doing it the right way, or if there could be a better (more efficient) way?

Here's the loop code:
loop: # Get a random background from the array my $new = $backs[int(rand(@backs))]; # Set the background to something new system($command . ' --type=string -s '. $gconf_item . ' \'' . $new . ' +\''); if ($mode eq "running") { sleep($interval*60); goto("loop"); } # end-if

Here's the script for anyone who's interested:
#!/usr/bin/perl use strict; use warnings; use XML::Simple; srand; # Mode to run #my $mode = "startup"; my $mode = "running"; # Interval to change at if $mode eq "running" my $interval = 60; # gconf vars my $command = "gconftool-2"; my $gconf_item = "/desktop/gnome/background/picture_filename"; # The location of the background xml file my $xmlfile = $ENV{HOME} . "/.gnome2/backgrounds.xml"; # Find the current background so we dont change to this my $current = `$command -g $gconf_item`; # Read in the xml file contents to get our list of backgrounds my $config = XMLin($xmlfile); # Something to store our filenames in my @backs; foreach my $name (keys %{$config->{wallpaper}}) { # Skip any that have a deleted flag in the XML file next if ($config->{wallpaper}->{$name}->{deleted} eq "true"); # Set the filename my $file = $config->{wallpaper}->{$name}->{filename}; # Ignore the current background and the (none) background next if (($file eq $current) || ($file eq "(none)")); # Store the name in our array push(@backs, $file); } # end-foreach loop: # Get a random background from the array my $new = $backs[int(rand(@backs))]; # Set the background to something new system($command . ' --type=string -s '. $gconf_item . ' \'' . $new . ' +\''); if ($mode eq "running") { sleep($interval*60); goto("loop"); } # end-if exit;

Thanks for your help.

Cheers,
Reagen

Replies are listed 'Best First'.
Re: how to loop
by Zaxo (Archbishop) on Feb 21, 2007 at 02:14 UTC

    How about this?

    while ($mode eq 'running') { my $new = $backs[int(rand(@backs))]; system($command . ' --type=string -s '. $gconf_item . ' \'' . $new + . '\''); sleep($interval*60); }
    That's not exactly equivalent to your code, but all sorts of adjustments are possible

    After Compline,
    Zaxo

      thanks. i guess this'll work:
      while (1) { # Get a random background from the array my $new = $backs[int(rand(@backs))]; # Set the background to something new system($command . ' --type=string -s '. $gconf_item . ' \'' . $new . + '\''); last if ($mode ne "running"); sleep($interval*60); } # end-while
        From a maintenance perspective, I like the previous example a bit more because you can understand the exit condition by reading the while line. Even a non-coder could understand it: while ($mode eq 'running'); keep going while the mode equals 'running'.

        In your example, I have to search through the entire block and find the 'last if' line to figure out how you break out of the loop.

        They are basically equal, but I find the first example easier to read and understand.

Re: how to loop
by muba (Priest) on Feb 21, 2007 at 04:37 UTC

    ... (insert thoughtful face here) ... is it just me, or does $mode never change value inside the loop in any of the snippets posted so far?

      you're right. $mode never changes value during the running of the script. it's just set manually before running.
        Um... so if you edit the script so that $mode is set to something other than "running", the loop will execute just once, and the script exits; but if you edit the script so that $mode is set to "running", the script loops indefinitely until you kill it (^C or whatever). And that's the intended design?

        I would have expected that either (a) the value assigned to $mode would depend on @ARGV (or %ENV, or some other external input), or (b) there would be something inside the loop that could change the value of $mode, or else (c) each time you edit the script to change its behavior, rather than editing a variable assignment, you would just edit the loop condition -- e.g. do {...} while(0); vs. do {...} while(1);, or something to that effect. (Note that the former "do" block will execute exactly once, whereas the block in while(0){...} never executes.)

        As it is, I tend to share muba's puzzlement.

        (updated to fix spelling)

Re: how to loop
by andye (Curate) on Feb 21, 2007 at 18:52 UTC
    Hi Reagen, You might want to use a 'do while', which works like this:

    $a=0; do { print $a++, "\n" } while ($a < 10)

    Because it checks the condition at the end, it'll run the loop once through even if the condition is false to begin with (try changing the condition to $a < -10 and you'll see what I mean).

    You can find out more in the docs under perlsyn. HTH!

    Best wishes, a.

      Be careful though - you can't use loop directives (next, last, redo, etc.) in a do while.