Shlomi Fish <shlo...@shlomifish.org> writes: >> An 'if' is an 'if', and when putting 'last' in front of it changes it to >> something else, why isn't there a keyword for it like 'lastif'? >> > > How so? > > last MYLABEL if $x == 5; > > does exactly the same as: > > if ($x == 5) { > last MYLABEL; > } > > Similarly: > > print "Hello\n" if $x == 5; > > does exactly the same as: > > if ($x == 5) { > print "Hello\n"; > } > > Where do you see that last is treated different by if?
It's the 'if' that changes when treated with 'last': my $x = 10; THIS: while($x) { if($x == 5) { print "Hello\n"; last THIS; } else { print "World\n"; } --$x; } works, but my $x = 10; THIS: while(1) { last THIS if($x == 5) { print "Hello\n"; } else { print "World\n"; } --$x; } doesn't work. >> > Yes, Perl Best Practices recommends to always use a label with "last", >> > "redo" or "next" to avoid problems where you add or remove more inner >> > loops. I agree with it here: >> > >> > http://perl-begin.org/tutorials/bad-elements/#flow-stmts-without-labels >> >> It seems to me that adding another loop can break the code no matter >> whether you use labels or not. > > Well, I don't understand you here. My point is that in any case, you need to make sure to exit out of the right loop. For this, I fail to see how it matters whether you have used labels or not. You can always exit out of the wrong loop after modifying your code. The only difference with labels is that the modified code will exit out of the same loop as before (assuming you didn't modify the labels and loops associated with the labels). This may be the right loop to exit or not. Labels only help to exit out of the same loop as before the modification. They do not magically assure that you will always exit out of the right one. >> When we were still programming in BASIC, the using of 'goto' statements >> was considered very bad style. The using of labels in perl like this >> doesn't seem to be any different. Sure it can be useful, but if I had >> to use a label like this, I'd feel that I should put big fat warnings >> about it into a comment /because/ it is more likely to break the code >> when changes are made than not using a label like this is. > > How so? It's because when I use labels and modify the code, I need to check whether the code changed in such a way that due to the labels used, it still exits out of the right loops or not. When I always exit out of the innermost loop and thus don't use labels, the code will still exit out of the innermost loop after it has been changed. (Which may be the right loop to exit out of or not, but that is a different problem because I have eliminated the possibility that the labels screw up my code.) When I abuse labels to exit out of loops other than the innermost one, I have created what used to be called spaghetti code and was considered bad style, like you would do with the use of "goto" statements in BASIC. >> > Yesterday, I introduced a bug or two in my C code (which were luckily >> > caught by the tests) because I removed a loop and as result a few C >> > "continue" statements (which is equivalent to Perl 5’s "next") terminated >> > the more previously outer loop. It is possible that the C compilers of >> > http://en.wikipedia.org/wiki/GNU_Compiler_Collection and >> > http://en.wikipedia.org/wiki/Clang have extensions for doing that similar >> > to >> > what Perl has, but I couldn't find them yet. >> >> Spaghetti code in C? ;) Maybe I'm overlooking something; do you have a >> good example where using a label prevents you from breaking the code? > > I just gave you one. I had: > > for ( LOOP() ) > { > for (OTHER_LOOP() ) > { > x = LOOKUP_BASED_ON_OTHER_LOOP(); > . > . > . > if ( COND1() ) > { > continue OTHER_LOOP; > } > . > . > . > if ( COND2() ) > { > continue OTHER_LOOP; > } > > PRODUCTION_STUFF_HERE() > > } > } > > And it became: > > for ( LOOP() ) > { > x = LOOKUP_BASED_ON_LOOKUP_TABLE(); > . > . > . > if ( COND1() ) > { > continue OTHER_LOOP; > } > . > . > . > if ( COND2() ) > { > continue OTHER_LOOP; > } > > PRODUCTION_STUFF_HERE() > } > > Only in this case, continue was a standalone keyword and I didn't see that > OTHER_LOOP disappeared and so it did it for the main «for ( LOOP() )» So if you had used labels, you'd have received an error message like "OTHER_LOOP is undefined" and you'd have seen the problem right away. You kinda have a special case here in which you always exit out of the innermost loop. In that case, I would agree that labels can be useful. What I've been thinking about is exiting from other loops than the innermost one. It doesn't occur to me to use labels to exit out of the innermost loop (or to continue another one than the innermost one) because it creates spaghetti code, and neither 'break;', nor 'continue;' in C do that :) If you had for ( LOOP() ) { for (OTHER_LOOP() ) { x = LOOKUP_BASED_ON_OTHER_LOOP(); . . . if ( !COND1() ) { . . . if ( !COND2() ) { PRODUCTION_STUFF_HERE() } } } } ... you won't need 'continue' statements, and the code would still be fine after removing the 'OTHER_LOOP()'. That's how I would do it unless there's a really good reason to 'continue'. With labels, consider this: THIS: while($something) { ... last THIS if($that); ... } which might become: THIS: while($something) { ... MORELOOP: while($foobar) { ... last THIS if($that); ... } ... } ... and things may go wrong /because/ you used a label (and forgot to change it). It can still be right. In any case, the label doesn't magically fix it, it either breaks it or not. You could argue that the label makes it clear which loop is exited. When there is no label used, it is as clear because the innermost one is exited. >> >> The really confusing part is/was what the 'last' statement actually does >> >> when used in conjunction with an 'if'. Apparently it re-defines the >> >> meaning of 'if' --- or there are multiple versions of 'if' in perl: > > The code: > > [CODE] > print "Hello" if ($foo) { > print $bar; > } > else { > print $test; > } > [/CODE] > > Won't work either. last is not special in the treatment of a trailing if > statement modifier - you can put any single statement you want there. This means that 'last' isn't the only statement that re-defines 'if'. Or there are still two different 'if's. >> > Also, please don't use the ternary conditional operator for side-effects, >> > see: >> > >> > http://perl-begin.org/tutorials/bad-elements/#ternary_operator_instead_of_if_else >> >> What is considered as 'side effects'? Should one use it without effects? > > Well, in general the ternary operator should be used to return either of two > values (to an assignment, or the return value of a subroutine), not call > function such as print, or do assignment itself, which is better reserved for > if/else. Like $a = ( ($b == 5) ? 1 : 2);? Is that better than {$a = 2; if($b == 5) a = 1;} or $a = ($b == 5) + ($b != 5) << 1; or $b == 5 ? $a = 1: $a = 2;? What's running the fastest? >> >> So how about: >> >> >> >> >> >> { >> >> print "123\n"; >> >> print "456\n"; >> >> } if $color eq "blue"; >> >> >> >> >> >> That doesn't compile, so this preappended 'if' is like implemented only >> >> half way in both directions :/ >> > >> > A trailing if applies only to one statement (by design). >> >> That's inconsistent design since statements in such brackets are >> effectively one statement ... > > Not in Perl 5, where they are considered a block. Perl 6 is a different > matter. Well, in contexts like this, I always considered a block as a single statement ... >> > You can use: >> > >> > do { >> > print "123\n"; >> > print "456\n"; >> > } if $color eq "blue"; >> > >> > but in this case I believe a normal conditional with the condition at the >> > beginning would be preferable. >> >> I agree --- the whole concept of syntactically appending stuff that is >> logically prefixed seems a bit questionable. But then, nobody forces >> anyone to use it. > > Yes. My personal style is to usually avoid statement modifiers, except in > command line one-liners/etc. It's somewhat tempting to use them, though ... BTW, is there an intrinsic way to figure out if the start of a loop has been reached because the loop was through or because 'redo' was executed? 1 THIS: 2 while($loop) { 3 ... 4 if($condition) { 5 ... 6 redo THIS; 7 } 8 ... 9 } The 'redo THIS;' is like a 'goto 3'. Now in line 3, I want to know whether I got there because 'redo' was executed or because the loop went through. Something like: 1 THIS: 2 while($loop) { 3 if( !redone) ... ; 4 if($condition) { 5 ... 6 redo THIS; 7 } 8 ... 9 } ... because there might be an operation in line 3 which I only want to do once for the current iteration (like 'chomp $_;'). I could figure it out through using variables, but isn't there something built in to check for this? -- "Object-oriented programming languages aren't completely convinced that you should be allowed to do anything with functions." http://www.joelonsoftware.com/items/2006/08/01.html -- To unsubscribe, e-mail: beginners-unsubscr...@perl.org For additional commands, e-mail: beginners-h...@perl.org http://learn.perl.org/