5 Replies - 3947 Views - Last Post: 26 July 2013 - 02:39 PM

#1 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1107
  • View blog
  • Posts: 6,924
  • Joined: 07-September 06

Consequences of 'last'?

Posted 24 July 2013 - 10:02 PM

I was working on mule (see the share your project thread for more details there) and came to a point where breaking out of a loop would be useful. I have used last in the past without problem, but also in simpler cases where I didn't care about variable values as they were all being (rightly) destroyed after the loop was completed.

However, in this particular instance, I have found that my variable (which based on the scope which it is defined in should still be available) is being cleared out before I can make real use of it (am searching through a triple dimension array -- well a looping through hash of arrays of hashes for each argument sent in to a function) and am calling last twice to get to the outer-most loop and do the real functionality.

Based on this http://perldoc.perl....tions/last.html it doesn't appear that there should be any consequences of the last keyword being called, however it appears to me that there are.

Now, I got around this by adding in a separate function to do the installation work, but I was wondering if (as the title suggests) last has consequences?

Is This A Good Question/Topic? 0
  • +

Replies To: Consequences of 'last'?

#2 dsherohman  Icon User is offline

  • Perl Parson
  • member icon

Reputation: 226
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Consequences of 'last'?

Posted 25 July 2013 - 03:44 AM

From your description of the situation, that definitely shouldn't be happening, although it sounds like you're trying to break out of more than one loop at the same time, in which case you might want to use last LABEL instead of a plain last.

Can you post the problem section of code here, preferably with (just) enough supporting structure around it so that we can run it and see the problem happen for ourselves?
Was This Post Helpful? 0
  • +
  • -

#3 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1107
  • View blog
  • Posts: 6,924
  • Joined: 07-September 06

Re: Consequences of 'last'?

Posted 25 July 2013 - 08:34 AM

Alright, I have cut down mule to the base functions needed to easily represent the problem (along with the base files needed to get it working up to that point. Here they are:

First - You will need perl-JSON and git installed since mule uses them.

mule:
#!/usr/bin/perl

use JSON;
use warnings;
use strict;

my @args = @ARGV;
sub install(@);
my %commands = (
	"install" => \&install
);
sub _main(@){
	my @argv = @_;
	my $cmd = shift(@argv);
	if(!$cmd){
		printf("Please put in a command\n");# FIXME - Should be a help message
		return;
	}
	if($commands{$cmd}){
		&{$commands{$cmd}}(@argv);
	}
	else{
	}
}
_main(@args);


sub install(@){
	my $jsonString = "";
	my $jsonRef = 0;
	my $dirty = 0;

	`touch .mule`;
	open(REPO_LIST, "<.mule");
	while(<REPO_LIST>){
		$jsonString .= $_;
	}
	close(REPO_LIST);
	if(!$jsonString){
		printf("Empty .mule file, please use 'mule repo add' to add repository mirrors\n");
		return;
	}
	else{
		$jsonRef = decode_json($jsonString);
	}

	foreach my $bag (@_){
		my $found = 0;
		my $repoName;
		my $repoBag;
        my $repoJsonRef;
		foreach $repoName (keys(%{$jsonRef->{"repos"}})){
			my $repoJsonString = "";
			open(REPO_BAGGAGE, "<$repoName/baggage");
			while(<REPO_BAGGAGE>){
				$repoJsonString .= $_;
			}
			close(REPO_BAGGAGE);
			$repoJsonRef = decode_json($repoJsonString);
			foreach $repoBag (@{$repoJsonRef}){
				if($repoBag->{"name"} eq $bag){
					$found = 1;
                    printf("1 Found bag " . $repoBag->{"name"} . "\n");
					last;
				}
			}
			if($found){
                printf("2 Found bag " . $repoBag->{"name"} . "\n");
				last;
			}

		}
		if($found){
            printf("3 Found bag " . $repoBag->{"name"} . "\n");
			$dirty = 1;
        	push($jsonRef->{"bags"},  $repoBag);
        	if(!(-d $bag)){
        		printf("3: Repo bag found: " . $repoBag->{"name"} . "\n");
        		my $bagURL = $repoBag->{"repo"};
        		`git clone $bagURL`;
        	}
        	else{
        		`cd $bag; git pull origin master`;
        	}
        	my $installer = $repoBag->{"installer"};
        	`cd $bag; ./$installer`;
		}
		else{
			printf("Bag $bag could not be found\n");
		}
	}

	if($dirty){
		my $repoString = encode_json($jsonRef);
		open(REPO_FILE, ">.mule");
		printf(REPO_FILE "$repoString");
		close(REPO_FILE);
	}
}



.mule:
{"repos":{"_repos":"path_Here/_repos.git"},"bags":[{}]}


_repos/baggage:
[{
	"name": "example",
	"version": 1.0,
	"description": "A quick example BAG",
	"keywords": [
		"example",
		"mule"
	],
	"author": "James Blades",
	"dependencies": {
		"example-core": "1.2"
	},
	"devDependencies": {
	},
	"repo": "git@example.com/example",
	"installer": "example.installer.sh",
	"uninstaller": "example.uninstaller.sh",
	"license": "MIT"
}]


NOTE - You will get a git error when running it, because the example repository doesn't exist and you attempt to clone a blank string anyways.

How to run it:
./mule install example (mule must be executable)
Was This Post Helpful? 0
  • +
  • -

#4 dsherohman  Icon User is offline

  • Perl Parson
  • member icon

Reputation: 226
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Consequences of 'last'?

Posted 26 July 2013 - 05:04 AM

So this is the loop where you're hitting the problem, right?
foreach $repoName (keys(%{$jsonRef->{"repos"}})) {
  # ...
  foreach $repoBag (@{$repoJsonRef}) {
    if ($repoBag->{"name"} eq $bag) {
      $found = 1;                       
      printf("1 Found bag " . $repoBag->{"name"} . "\n");
      last;                             
    }                           
  }                     
  if ($found) {         
    printf("2 Found bag " . $repoBag->{"name"} . "\n");
    last;                       
  }                     


The inner printf ("1 Found...") works, the outer printf ("2 Found...") doesn't.

Turning this into a self-contained minimal failing case, we get:
#!/usr/bin/perl

use warnings;
use strict;

my $found;
my $bag = 'foo';
my $repoName;
my $repoJsonRef;
my $repoBag;

foreach $repoName (qw(foo bar baz)) {
  $repoJsonRef = [ { name => 'foo' } ]; 
  foreach $repoBag (@{$repoJsonRef}) {
    if ($repoBag->{"name"} eq $bag) {
      $found = 1;                       
      printf("1 Found bag " . $repoBag->{"name"} . "\n");
      last;                             
    }                           
  }                     
  if ($found) {         
    printf("2 Found bag " . $repoBag->{"name"} . "\n");
    last;                       
  }                     
}


When run, it gives the output
1 Found bag foo
Use of uninitialized value in concatenation (.) or string at ./minimule line 22.
2 Found bag



Although you're right in your initial statement that the actual data is still there, a for (@list) loop's control variable is restored to its previous value when the loop exits (regardless of whether or not the loop exits due to calling last). When the outer loop tries to access $repoBag->{"name"}, $repoBag has been restored to undef. You can see this more clearly by changing line 10 from my $repoBag; to my $repoBag = 'out of the loop';, in which case running the program produces the output
1 Found bag foo
Can't use string ("out of the loop") as a HASH ref while "strict refs" in use at ./minimule line 22.



My recommendation to avoid similar problems in the future would be to always declare your variables in the smallest possible scope:
#!/usr/bin/perl

use warnings;
use strict;

my $bag = 'foo';

foreach my $repoName (qw(foo bar baz)) {
  my $found;
  my $repoJsonRef = [ { name => 'foo' } ];
  foreach my $repoBag (@{$repoJsonRef}) {
    if ($repoBag->{"name"} eq $bag) {
      $found = 1;
      printf("1 Found bag " . $repoBag->{"name"} . "\n");
      last;
    }
  }
  if ($found) {
    printf("2 Found bag " . $repoBag->{"name"} . "\n");
    last;
  }
}


By declaring the control variables inline to the loop statements, this has become a compile-time error instead of a run-time error:
Global symbol "$repoBag" requires explicit package name at ./minimule line 19.
Execution of ./minimule aborted due to compilation errors.


...and hopefully one whose cause is more immediately obvious from the error message.
Was This Post Helpful? 1
  • +
  • -

#5 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1107
  • View blog
  • Posts: 6,924
  • Joined: 07-September 06

Re: Consequences of 'last'?

Posted 26 July 2013 - 09:38 AM

Ah, interesting, so it is actually a consequence of the way perl does loops that I was fighting against. I come from C/C++ style languages which don't reset variables when loops exit (unless the variable was declared within the scope of that loop).

Thank you for your time.
Was This Post Helpful? 0
  • +
  • -

#6 dsherohman  Icon User is offline

  • Perl Parson
  • member icon

Reputation: 226
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Consequences of 'last'?

Posted 26 July 2013 - 02:39 PM

View PostBetaWar, on 26 July 2013 - 05:38 PM, said:

Ah, interesting, so it is actually a consequence of the way perl does loops that I was fighting against. I come from C/C++ style languages which don't reset variables when loops exit (unless the variable was declared within the scope of that loop).

Thank you for your time.

No problem. I enjoy solving these puzzles. :D

One of the (usually) cool things Perl does with a for (LIST) loop is that the control variable is actually aliased to each value in the list it's looping over:
$ perl -E '$foo = "foo"; $bar = "bar"; for $loop ($foo, $bar) { $loop .= " looped" }; say $foo; say $bar;'
foo looped
bar looped


Since the control variable is being aliased rather than assigned to, I assume that's the reason that it gets localized and is restored when you exit the loop's scope.

But also note that this is specific to the for (LIST) construct. If you change to a while loop (or a C-style for ( ; ; ), although that doesn't seem applicable in this case), then all your variables should retain their values after the loop terminates.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1