Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

GOTO or not GOTO

by jflevi (Beadle)
on Jan 27, 2009 at 02:20 UTC ( [id://739087]=perlquestion: print w/replies, xml ) Need Help??

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

Good evening, Wise Monks, This apprentice is stumbling on a basic question.

I have written a short program to test different types of human reaction type; at the end I want to give the user the option of running the program another time; coming from a time where computers had no screen and no keyboard, my first idea was a GOTO label.

However I learned a while ago that GOTO is not "perliticaly correct".

I could wrap the main routine in a "do while" loop but it doesn't seem very elegant either ...

Any suggestions ???

Thanks !!!

Life is tough, it's tougher when you are dumb...

jfl

Replies are listed 'Best First'.
Re: GOTO or not GOTO
by GrandFather (Saint) on Jan 27, 2009 at 02:30 UTC

    There is almost never a need to use goto in modern programming languages with a plethora of good flow control structures. If your intent is "do this block of code while some condition pertains" then do while expresses that very nicely.

    If you don't find that a satisfying argument in the context of your code you better show us your code so we can debate specifics.


    Perl's payment curve coincides with its learning curve.
      There's never a need to use 'while' either, given there's a plethora of other good flow control structures as well. But from that doesn't follow we should shy away from 'while'. Perl gives the programmer a full toolbox, full enough to make almost any tool 'redundant', in the sense that there are enough other tools to not use a particular tool. But we don't use that argument to select some tools, and reject others.

      Personally, I use almost every tool* in the toolbox. Some more than others. I don't use goto often, but I do use it. For instance, in code like this:

      my $max_tries = 10; GETSTORE: my $url = ask_url(); my $status = getstore($url, $file); goto GETSTORE if $status == 200 && !-s $file && --$max_tries > 0; ... do stuff ... return $status;
      I find that more attractive than
      my $max_tries = 10; my $status; { my $url = ask_url(); $status = getstore($url, $file); redo if $status == 200 && !-s $file && --$max_tries > 0; } .. do stuff .. return $status

      *Tools I never use that I can think of right now: formats, 1-arg open, study, and /o.

        I'd write that as (untested)
        while ( uninteresting() ) {}; : : sub uninteresting { # I assume $file and $max_tries are globals my $url = ask_url(); my $status = getstore($url, $file); return ( $status == 200 && !-s $file && --$max_tries > 0 ); }

        You don't say why you find the goto version more attractive. I guess because of the separate declaration and initialization of $status. However:

        my $status; do { $status = getstore (ask_url (), $file); } while $status == 200 && !-s $file && --$max_tries > 0;

        makes it clear that there is a loop involved and what the conditions on the loop are. Having $status declared outside the loop makes it clear that $status is required after the loop has terminated.


        Perl's payment curve coincides with its learning curve.
      There is almost never a need to use goto in modern programming languages

      Right, but especially in Perl there are some cases where you can't get around goto, e.g. in AUTOLOAD!

        Well, it's called goto, but that's a little misleading. In many ways the use (often found in the context of AUTOLOAD) that you allude to is more akin to a magical call to a subroutine than to the conventional use of goto suggested by the OP.


        Perl's payment curve coincides with its learning curve.
Re: GOTO or not GOTO
by ww (Archbishop) on Jan 27, 2009 at 02:31 UTC
    For the purpose you've outlined, "perlitically correct" be damned! Go ahead and GOTO.

    But if you wish to expand your perl fu, you may wish to consider a wide variety of other mechanisms:

    • Dispatch table
    • A 'prompt_user' sub
    • Your own do while...
    • ... (TIMTOWTDI!)
Re: GOTO or not GOTO
by ikegami (Patriarch) on Jan 27, 2009 at 05:06 UTC
    do { ... } while should_repeat();
    would also do the trick.
Re: GOTO or not GOTO
by monarch (Priest) on Jan 27, 2009 at 06:06 UTC
    As others have said it is not about politics but about safety. And after enough experience a programmer learns that they are their own worst enemy, so anything you can do to lessen the incidence of problems is a bonus.

    The main problem with goto is not understanding potential side-effects. If you goto a particular location then you run the risk of using code that assumes a different state to the one you arrive in.

    My issue with goto has always been the stack - I will (albeit rarely) use goto if it does not involving changing contexts (i.e. never jumping into or out of a set of curly braces). But I really don't know the implications to memory usage and contexts when I do something like the following (in C or in Perl):

    for ( my $i = 0; $i < 10; $i++ ) { my $j = $i; if ( $i == 5 ) { goto newlocation; } } newlocation: print( "jumped out" );

    Now my question is what happened to $j there? Is the compiler "smart enough" to know that I exited the loop and therefore the memory allocated to $j can now be reclaimed?

    Even worse, what happens when I use goto to jump between subroutines?

    It is because I don't know the answers to these questions that I do my best to avoid the use of goto (except when necessary and not involving context changes).

      Is the compiler "smart enough" to know that I exited the loop and therefore the memory allocated to $j can now be reclaimed?

      Perl doesn't reclaim lexicals, it just clears them. And yes, $j gets cleared.

      sub DESTROY { print("DESTROYED\n"); } for ( my $i = 0; $i < 10; $i++ ) { my $j = bless({}); if ( $i == 0 ) { goto newlocation; } } newlocation: print( "jumped out" );
      DESTROYED jumped out
      for ( my $i = 0; $i < 10; $i++ ) { my $j = $i; if ( $i == 5 ) { goto newlocation; } } newlocation: print( "jumped out" );
      Now my question is what happened to $j there? Is the compiler "smart enough" to know that I exited the loop and therefore the memory allocated to $j can now be reclaimed?
      Imagine you had written:
      for ( my $i = 0; $i < 10; $i++ ) { my $j = $i; if ( $i == 5 ) { last; } } print( "jumped out" );
      you have code that does exactly the same. I presume you have the same worries about $j, and will hence decide to never use 'last'?

      Or else, could you indicate why you have worries about the first construct, but not the last?

      Even worse, what happens when I use goto to jump between subroutines?
      Had you actually bothered to read past the first sentence of the document you link to, you would have known the answer. Here's the second sentence of the document: It may not be used to go into any construct that requires initialization, such as a subroutine or a foreach loop.
      It is because I don't know the answers to these questions that I do my best to avoid the use of goto
      Since I've now answered the questions, are you now going to start using goto?

      I have to admit, you weren't arguing against the use of goto in particular. You were arguing against using constructs you don't know the details about. That's a smart thing. The wise thing would be to actually learn about them instead of bragging ones ignorance.

        The big difference between last and next is that those functions give deterministic answers to the compiler as to code flow. On the other hand goto may not offer as clear an indication of the target; although given a label the compiler may be able to work out whether the destination is outside the given closure anyway.

        So here's the next example, what does the following do:

        for ( my $i = 0; $i < 10; $i++ ) { my $j = $i; if ( $i == 5 ) { goto nextpoint; } } for ( my $k = 0; $k < 10; $k++ ) { my $m = $k; nextpoint: print( "$m\n" ); }

        Well I tried it, but the point is I didn't know what it would do before hand. If that is "bragging one's ignorance" then so be it. Rightly or wrongly I came from a C background where if you tried risky behaviour like this you got into real trouble. Perl is far more forgiving.

        I do prefer to program in a manner that has a degree of portability between languages; as I said before, goto is risky: if you consider yourself an expert in the language at the time and the compiler behaviour and implications (if any) to the stack then by all means go ahead. I'll stick to what I know is safe.

Re: GOTO or not GOTO
by apl (Monsignor) on Jan 27, 2009 at 05:36 UTC
    It's not a question of being "perliticaly correct". The intention of your code is inherently less readable if you use a goto. In addition, placing your code in a subroutine
    • makes your intentions obvious
    • enables you to write tests for the correctness of the code that would be executed twice
    • prevents problems that might arise from variables that aren't reset in you looped code
GOTO in C, not in Perl
by grinder (Bishop) on Jan 27, 2009 at 10:43 UTC

    There are valid performance reasons for using goto in C: it compiles down to a single instruction and is the nicest way to build a state machine.

    On the other hand, using goto in Perl is very slow: the interpreter has to walk up and down the optree to look for the label. Since Perl is so much slower anyway, you may as well use a dispatch table (of coderefs in a hash) instead, if it's a state machine you're after.

    Anyway, getting back to the OP, an infinite loop (while (1) {...}) with an explicit break (last) is preferable to a goto back to the beginning, or a do while.

    • another intruder with the mooring in the heart of the Perl

Re: GOTO or not GOTO
by graff (Chancellor) on Jan 27, 2009 at 06:16 UTC
    My suggestion would be try something out, and then post it here to see how monks react to it. If you try something that involves using GOTO, some are likely to comment on how ugly/obtuse/unmaintainable that appears to be, and will offer more elegant/sane/well-structured alternatives. If you try some other approach but in some clumsy fashion, you'll get the same helpful advice. If you post something that turns out to be a really good solution, you'll get more XP.

    I used GOTO rather a lot in my grad-student days, when I was programming in Fortran; I struggled a bit to break that habit as I moved to C, but it worked. These days, after 14 years writing Perl, I consider myself unable to use GOTO competently, and I'm happy to report that I don't miss it at all.

Re: GOTO or not GOTO
by dHarry (Abbot) on Jan 27, 2009 at 08:22 UTC

    From a high level perspective you don't need a goto statement. The argument that it can lead to unreadable code is a good one. But just because misuse of the goto statement can be harmful, it doesn't mean that it shouldn't be part of your toolbox.

    The goto doesn't kill code, bad programming does!

    There is a business case for the goto, see for example "argument for goto".

      The arguments in that essay are a poor rendition of the arguments that Knuth gave in Structured Programming with Go To Statements. However if you read that essay carefully you will realize that modern exception handling is better for handling errors than goto, and the remaining efficiency points are all addressed completely by labeled loop control. (In fact Knuth refers to a paper proving the second point, however at the time that point was theoretical only since no languages had that feature back then. However Perl does.)

      That is not to say that there are not cases where goto is still useful. But they are very rare. I can only think of 3 that I have ever seen in Perl, and none of them were in code that I wrote.

      Bravo! I don't feel particularly compelled to justify my use of goto here for example.
        I would personally replace it with a redo on a bare block. That bare block would also be a hint to indent the potentially redone section of code, which naturally draws attention to that control structure.

        General rule of thumb. Any normal use of goto can be replaced by an entirely equivalent loop control statement, possibly using labels when breaking out of multiple levels of loops. Unless there is a good reason not to, I prefer that approach.

Re: GOTO or not GOTO
by ww (Archbishop) on Jan 27, 2009 at 08:25 UTC

    OK, awreddy! The wisdom elsewhere in this thread prompts me to expand what appears to be my minority response to the OP:

    First, for clarity, allow me to amend "For the purpose ... ahead and GOTO." to read: "For the NARROW purpose you've outlined, perlitical correctness is not the issue. "Perlitically correct" be damned! If it works, go ahead and GOTO (sic) (but don't get in the habit)!

    Yes, even that runs counter to convention wisdom (and I readily acknowledge that the conventional wisdom is wise. (In fact, the bit of nonsense below represents the first time I've even considered using goto in years.)

    But I read the OP, the Seeker has a suite of tests of "human reaction," each of whose members stands alone. IOW, I conceive each test to be fundamentally self-contained and (perhaps) presented in a fixed sequence; that his offer to re-run the tests would occur only upon completion of the suite.

    In such a case, concerns about spaghetti code, readability, resetting of variables, and all the other usual problems with goto are (or can be, in a(n otherwise, if you must) well-written program can be moot.

    The script below, while not reflecting precisely the constraints above, may invite comments... or even second thoughts???

    use strict; use warnings; my @array = qw/a b c/; START: for my $item(@array) { my $sub_call = "&" . $item; eval ($sub_call); } sub quit { print "Would you like to do that again? 'y' or 'n'\n"; if (<STDIN> =~ /y/) { goto START; }else{ print "OK, done.\n"; exit; } } sub a { print "Type 'blue' to continue:\n"; if (<STDIN> =~ /blue/i) { return 1; } else { quit(); } } sub b { print "Cross your fingers and type 'foobar' to continue:\n"; if (<STDIN> =~ /foobar/i) { return 1; } else { quit(); } } sub c { print "To quit (prematurely), type 'no'\n"; if (<STDIN> =~ /no/) { exit; } else { goto START; } }

    For "nonsense" constructed specifically to use a goto, this seems to me (YMMV) clear and utterly without 'strange actions at a distance.'

    FTR, in NO way does it rebut the correctness of deprecating goto; rather, it's intended as a reminder that there are -- occasionally -- times to violate conventional wisdom and ways to sidestep the underlying evil that norm seeks to avoid.

      Lets restructure that code just a little:

      use strict; use warnings; my @array = (\&blue, \&foobar, \&no); do { eval {$_->() for @array}; } while $@; sub quit { print "Would you like to do that again? 'y' or 'n'\n"; die if <STDIN> =~ /y/; print "OK, done.\n"; exit; } sub blue { print "Type 'blue' to continue:\n"; return 1 if <STDIN> =~ /blue/i; quit(); } sub foobar { print "Cross your fingers and type 'foobar' to continue:\n"; return 1 if <STDIN> =~ /foobar/i; quit(); } sub no { print "To quit (prematurely), type 'no'\n"; exit if <STDIN> =~ /no/i; die; }

      Shorter, cleaner, no gotos and, IMO, clearer. Now, why did we need those gotos again?


      Perl's payment curve coincides with its learning curve.
Re: GOTO or not GOTO
by shmem (Chancellor) on Jan 27, 2009 at 09:17 UTC

    Through all my perl programming, I have used goto LABEL only once - to show what hell is like. (Magic goto is another thing, and not for your audience.) Almost every time there's something wrong with your program's structure if you use goto LABEL. There's next, last and redo for control structures; and jumping around in code with goto is - lack of structure.

Re: GOTO or not GOTO
by derby (Abbot) on Jan 27, 2009 at 12:16 UTC

    Sigh .... 41 years after Goto Considered Harmful and we still have a *long* discussion about it. Now, before you call me a cargo cultist and tell me all the places a goto is useful, yes I know those but I also know that if if you have to ask 'is goto is correct' for your situation, it isn't.

    -derby
Re: GOTO or not GOTO
by swampyankee (Parson) on Jan 27, 2009 at 13:32 UTC

    I write software in Fortran; I've not needed to use a goto since about 1987 8-)


    Information about American English usage here and here. Floating point issues? Please read this before posting. — emc

Re: GOTO or not GOTO
by zentara (Archbishop) on Jan 27, 2009 at 13:02 UTC
    See Reusable threads demo for where I use goto to break out of deeply nested loops, and a way to avoid it, in TGI's response. I know goto's are considered bad..... but they are sooooo easy :-)

    I'm not really a human, but I play one on earth Remember How Lucky You Are

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://739087]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (4)
As of 2024-03-29 01:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found