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

Brothers and Sisters, I'm a bit confused of the use of or as a control structure. My understanding is that the lvalue is a block which is executed and evaluated, and if it fails the code block following is executed. Today I was writing something and attempted the following, which tries first to execute plan A and failing that plan B.
unless ($dontdothis) { dofistcritical() or { dobackupcritical() or { die "main and backup failed"; } dobackupremainder(); exit 0; } dofirstremainder(); exit 0; }
Das ist Verboten!
I'm still pondering the syntax of why it fails.
Cheers,
Andy.

Update: Just to make the intention perfectly clear, I wish to use OR as a fallthough operator in the style of
tryfirstchoice() or trysecondchoice() or trynextchoice or .... trylast +choice();
such that subs are evaluated in left to right order. The first sucessful sub completed will exit and complete its second (non critical) section.

For the record the final cut looks uncannily like what simonm posted. The use of do to disambiguate block boundaries makes life easy.

Replies are listed 'Best First'.
Re: Nested OR as a control structure
by diotalevi (Canon) on Jun 02, 2004 at 22:54 UTC

    You were looking for if/else. You'd use or to string together multiple conditionals.

    if ( $dothis ) { if ( dofistcritical() ) { dofirstremainder(); } elsif ( dobackupcritical() ) { dobackupremainder(); } else { die "main and backup failed"; } exit 0; }
Re: Nested OR as a control structure
by BrowserUk (Patriarch) on Jun 02, 2004 at 23:59 UTC

    If you really want do this (why?:), you can, and without resorting to subs or do blocks--but I wouldn't advise it. T'is fun though:)

    #! perl -slw use strict; my @result; sub dofirstcritical { print 'do first critical returning ', $result[ 0 ]; $result[ 0 ] } sub dobackupcritical { print 'do backup critical returning ', $result[ 1 ]; $result[ 1 ] } sub dobackupremainder{ print 'backup remainder done' } sub dofirstremainder { print 'first remainder done' } for my $result ( [ 0, 0 ], [ 0, 1 ], [ 1, 0 ], [ 1, 1] ) { @result = @$result; eval{ dofirstcritical() and ( dofirstremainder(), die 'Exiting 0' ) or ( ( dobackupcritical() or die "main and backup failed" ) and ( dobackupremainder(), die 'Exiting 0' ) ) }; print $@, $/; } __END__ P:\test>temp do first critical returning 0 do backup critical returning 0 main and backup failed at P:\test\temp.pl line 15. do first critical returning 0 do backup critical returning 1 backup remainder done Exiting 0 at P:\test\temp.pl line 15. do first critical returning 1 first remainder done Exiting 0 at P:\test\temp.pl line 15. do first critical returning 1 first remainder done Exiting 0 at P:\test\temp.pl line 15.

    Note: The eval block is only there to captiure and record the dies and simulated exits.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
Re: Nested OR as a control structure
by eric256 (Parson) on Jun 02, 2004 at 22:56 UTC

    Well i'm not sure i understand what you expect that to do. Or doesn't take a code block or a reference on the right, it takes an expression. So you can't do {} or sub {}. I think your control structure is similar to the following but i'm not realy sure. Also in the future you should consider includeing whatever errors you get because "I'm still pondering the syntax of why it fails." isn't very discriptive. The following code emulates how i read your example but is probably not what you had in mind.

    use strict; use warnings; sub test1 {print "Hello\n"; return 0;} sub test1remainder {print "Bye\n";return 1;} sub test2 {print "\tHello2\n"; return 0;} sub test2remainder {print "\tBye2\n";return 1;} sub test3 {print "\t\tHello3\n"; return 1;} sub test3remainder {print "\t\tBye3\n";return 1;} unless(0) { if (test1) { test1remainder and exit 0}; if (test2) { test2remainder and exit 0}; if (test3) { test3remainder and exit 0}; } __DATA__ C:\test>perl or.pl Hello Hello2 Hello3 Bye3

    ___________
    Eric Hodges
Re: Nested OR as a control structure
by parv (Parson) on Jun 02, 2004 at 23:17 UTC

    Did you try something like this...

    #!/usr/local/bin/perl use strict; use warnings; my $p = 9; if ( $p ) { sub { printf "$p < 3 ? %s\n" , $p < 3 ? 'yes' : 'no'; return 0; }->() or sub { printf "$p > 3 ? %s\n" , $p > 3 ? 'yes' : 'no'; return 0; }->() or print "I forgot my Math: $p <!> 3!\n" ; }

    In other words, if your subs return appropriate truth values, then your posted example should work. Mind you can bundle your subs w/ exit() in anonymous sub references similar to as shown above.

      Ah this is interesting, thanks Parv. Im playing with your anonymous sub refs type thing now. Im returning undefs from failed subs. Encouraging that you also think it should work.
      I wandered into writing it quite naturally, it seemed intuitively right.
      Thanks to the other posters for your thoughts too. I was specifically refering to the syntactic usage of OR even if the question sounded vague. Maybe it is more subtle than it seems, the expression (which I call a block) must behave as the return value of the sub. Damn my C legacy.
Re: Nested OR as a control structure
by andyf (Pilgrim) on Jun 03, 2004 at 00:19 UTC
    Thanks for all your replies.
    That's very thorough little test harness there Browser, nice. I think your going to beat me when you see that the nub of the question was just all about return values. This finally works
    #!/usr/bin/perl dofistcritical() or dobackupcritical() or dofailed(); sub dofistcritical {if (int rand 5) {print "hooray it worked first time!\n"} else {return 0}} sub dobackupcritical {if (int rand 5) {print "First time failed but luckily the backup worked...phew\n"} else {return 0}} sub dofailed {if (int rand 5) {print "Main failed. Backup Failed. You have 3 minutes to reach a +safe distance\n"} else {return 0}}
    I'm not exactly sure where it was failing before, but the complicated blocks to force critical and non-critical areas must have had me seeing things, from here I can work the critical/non-critical sections back in.
    Cheers all.
Re: Nested OR as a control structure
by dave_the_m (Monsignor) on Jun 03, 2004 at 09:53 UTC
    Others have pointed out better ways of doing this; for the record I'll just explain how Perl was seeing your code. Perl expects an expression to follow 'or', so
    ... or { ....; ... }
    is interpreted as an anonymous hash constructor, and the semicolon in the middle of it is then a syntax error. Also, no-one seems to have mentioned 'do'; this allows you to use a block of code as an expression, eg
    .... or do { statement; statment; statement_with_final_value }
    But most of the time you're still better off using if/else.

    Dave.

      Also, no-one seems to have mentioned 'do'...

      Yup, that's what I was thinking.

      I've become increasingly fond of the do-block syntax; here's the OP's code with the two "do"s added where needed:

      unless ($dontdothis) { dofistcritical() or do { dobackupcritical() or do { die "main and backup failed"; }; dobackupremainder(); exit 0; }; dofirstremainder(); exit 0; }
Re: Nested OR as a control structure
by bobn (Chaplain) on Jun 03, 2004 at 03:20 UTC
    So if you want to use:
    tryfirstchoice() or trysecondchoice() or trynextchoice or .... trylast +choice();
    then why not just do so?
    #!/use/bin/perl -lw tryfirstchoice() or trysecondchoice() or trylastchoice(); sub tryfirstchoice { print "tryfirstchoice"; return; } sub trysecondchoice { print "trysecondchoice"; return 1; } sub trylastchoice { print "trylastchoice"; return 1; } <code> Running this produces: <code> # perl or.pl tryfirstchoice trysecondchoice #

    --Bob Niederman, http://bob-n.com

    All code given here is UNTESTED unless otherwise stated.

Re: Nested OR as a control structure
by TomDLux (Vicar) on Jun 03, 2004 at 02:24 UTC

    or is identical to the operator || except that with ||, function calls need parentheses, while with or they don't.

    # this reports a problem if func() fails. func $arg1, $arg2 or warn "error invoking func()"; # so does this func( $arg1, $arg2 ) || warn "error invoking func()"; # Not what you might expect # this invokes func( $arg1, ($arg2 || warn "error invoking func()" ); func $arg1, $arg2 || warn "error invoking func()";

    My interpretation is that as the use of paranthese-less function calls became increasingly common, and so did the use of error detection, there was a clash, leading the the invention of or.

    But you aren't dealing with expressions, you're dealing with blocks.

    You can keep the code in block form, and use if() or unless().

    Alternately, you could have the first main and alternate return values which distinguish which one ran. They already need to return true for success and false for failure ... just modify things so dofirstcritical() and dobackupcritical() return different true values. Then you can break the continuation out into a separate block.

    # At the top of the file in the configuration section my $first_backup = 1; my $backup_backup = 2; unless ($dontdothis) { my $status = dofistcritical() or dobackupcritical() or die "main and backup failed"; if ( $status == $first_backup ) { dobackupremainder(); } elsif ( $status == $backup_backup ) { dofirstremainder(); } else { die( Invalid status '$status' from first backup component" ); } }

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

      or is identical to the operator || except that with ||, function calls need parentheses, while with or they don't.
      there are other differences than that. consider:
      tina@lux:~> perl -wle' @a = qw(a b c); @b = @a or die; @c = @a || die; print "(@b) (@c)"' (a b c) (3)
      Aye Tom, For a while I suspected || and OR might be synonyms, it seems that || binds more strongly, so I get that 3rd example ok. Using $status to grab the result of whichever one finally ran is nice. I can use that.
      Cheers,
      Andy.