12
$\begingroup$

We all know goto is the spawn of satan. To learners though, it seems easy and quick. In simple, short program codes having a goto or two won't turn the code into unreadable spaghetti code. So students have a hard time understanding why they shouldn't use jump statements. Even if they believe that I (the teacher) strongly believe it is bad practice, they won't "internalize" this.

My current strategy is elegantly forgetting about jump statements altogether. I don't tell them these things exist, hoping by the time they discover, they'll be used to coding right. Not only Goto, but also loop or subroutine/function exit statements (although I do use these from time to time in the darkness of my room, but I always feel dirty when doing so).

Should I teach them jump statements? If yes, how do I make them feel just how bad they are? Give them a huge code with many jump statements and have them try to understand? What is your strategy?

$\endgroup$
  • 5
    $\begingroup$ This question is truly language specific, as norms are very different across different languages. You can't avoid GOTO in BASIC or JMP in assembly languages, and there exist (rather rare) occasions in Java when break statements can be good practice. What language is this instruction in? $\endgroup$ – Ben I. Jun 9 '17 at 14:38
  • 2
    $\begingroup$ Could you add a language? I'm trying to create an answer, but it's nearly impossible to formulate a response without one. The discussion in ASM is just completely different from the discussion in Java. $\endgroup$ – Ben I. Jun 9 '17 at 15:23
  • 4
    $\begingroup$ break, continue, etc. all have a place and a time. The trick is to learning when and why. I recommend showing the power as well as the danger in using them. I'm always leery when someone says "All X are bad" - like the guy who said he never used loops, only LINQ. Time and place... $\endgroup$ – AlG Jun 9 '17 at 17:30
  • 2
    $\begingroup$ The real reason GoTos became extinct: XKCD reference $\endgroup$ – user737 Jun 15 '17 at 15:14
  • 1
    $\begingroup$ We really do need a language referenced. GoTo is not always bad. Try using Basic without GoTo, or assembly without JMP, ...or JE. In fact, there are many industrial languages, used today, where GoTo is absolutely required. There are best practices for most languages, and they don't always agree. $\endgroup$ – Gorchestopher H Mar 7 '18 at 18:50
20
$\begingroup$

I don't believe that your question is entirely valid; some languages require jumping. The first principle, therefore, is to follow the norms of your language.

However, I suspect that you are asking about languages that discourage (but do not ban) jumping, such as Java, or C++. In these cases, I agree with Peter that the solution is to give them the negative learning experience of having to parse code with bad jumping usage.

I would only add two things to what he said:

  1. Do not teach about break statements and jump statements if you can avoid it until they really have have absorbed the norms of looping. This can take some time. If kids start using them earlier than you would like, simply tell the students that, while such statements have a time and place, please avoid using them for now.

  2. As an idea for an exercise, just make some code that has for loops and replace them with awful while(true) loops. For instance, consider this bit of foul excrement, which will print out the first 10 Fibonacci numbers:

    int daffyDuck = 10;
    int[] bugs = new int[daffyDuck];
    bugs[0] = 0;
    bugs[1] = 1;
    int whynot = -1;
    while(true){
        whynot = whynot+1;
        int whistlersmother = 0;
        if (whynot == 10) break;
        int animeRocks = 0;
        while(true){
            if (whynot-animeRocks <= 2) 
                whistlersmother += bugs[animeRocks];
            animeRocks++;
            if(animeRocks>whynot)
                break;
        }
        bugs[whynot] = whistlersmother;
        System.out.print(bugs[whynot]+",");
    }
    

Give it to your students, but don't tell them what it does. Give them a chance to attempt to trace it. Tell them that they only have 4 minutes (240 seconds!)1 to figure out what it does! Presumably, they will struggle mightily to figure it out, but mention that, if anyone thinks that they've solved it (and can back that up), not to call anything out, but to instead raise their hand. If anyone does call you over, ask the to whisper the answer into your ear, and if they get it right, ask them to silently try to re-write the code segment in a way that would be easier to follow.

Now, give the class a new version, with another 240 seconds. Do not mention that it is exactly the same code as before, but with nicely-defined loops (and modified variable names). Once again, tell them not to call out the answers. The process here should be the same.

    int n = 10;
    int[] f = new int[n];
    f[0] = 0;
    f[1] = 1;
    for(int c = 0; c < f.length; c++){
        int s = 0;
        for(int j = 0; j <= c; j++){
            if (c-j <= 2) 
                s += f[j];
        }
        f[c] = s;
        System.out.print(f[c]+",");
    }

And finally, give them this version, same 240 seconds, same procedure:

    int fib_nums_to_print = 10;
    int[] fibonacciNums = new int[fib_nums_to_print];
    fibonacciNums[0] = 0;
    fibonacciNums[1] = 1;
    for(int currentNum = 0; currentNum < fibonacciNums.length; currentNum++){
        int sum_so_far = 0;
        for(int j = 0; j <= currentNum; j++){
            if (currentNum-j <= 2) 
                sum_so_far += fibonacciNums[j];
        }
        fibonacciNums[currentNum] = sum_so_far;
        System.out.print(fibonacciNums[currentNum]+",");
    }

At this point, you can reveal, for those that hadn't figured it out yet, that all 3 of these bits of code were the same function. Ask them what made the later versions easier. They should be able to pinpoint the two problems pretty easily.

Point out that loops can grow to be very, very large - hundreds, or even thousands of lines of code long. Ask them why the while(true) version of the loop would be especially problematic in longer code examples. Ask them where, in their opinion, would it be best in such long code for the coder to make clear when the loop will actually end.

All of which brings us to the final principle: we want to be able to figure out, at least in 99.9% of the cases, the basic logic of what will cause a loop to terminate at the moment that the loop is declared, because otherwise, we are left hunting and guessing.

1 - Any time you mention the number of seconds along with the number of minutes, the kids suddenly feel time pressure. It's a very consistent trick to get the kids to focus on a short task.

$\endgroup$
  • 1
    $\begingroup$ Big thanks for the seconds tip, really clever! $\endgroup$ – vacip Jun 11 '17 at 14:39
  • $\begingroup$ Great answer but why no braces around if-bodies? $\endgroup$ – Ellen Spertus Jun 11 '17 at 14:47
  • $\begingroup$ I was imitating my students (j/k). In truth, I don't typically use extra braces unless they are needed. I don't like the extra use of vertical space, and I find that indentation already makes the purpose clear. $\endgroup$ – Ben I. Jun 11 '17 at 14:49
  • $\begingroup$ But we always want our programs to run until false. Then they should stop right away. $\endgroup$ – user737 Jun 15 '17 at 15:16
  • 1
    $\begingroup$ "Brace all" is a recognized best practice coding pattern. There are places like the old Bell Labs and its successors at which you get fired if you don't. Lack of bracing in the face of program update is a know bug generator. So @EllenSpertus comment should not be overlooked. Just do it. $\endgroup$ – Buffy Jul 11 '17 at 12:21
6
$\begingroup$

Make them suffer through debugging spaghetti code.

Come up with a few examples that make liberal use of the language features you don't want them to use. 100-150 lines of code with 1 or 2 small bugs because of these statements will be a nightmare for them to sort through. Make it a sort of game, too. Have students compete in groups to see who can debug the programs the quickest, and give them, say, three different ones to solve in a particular allotted amount of time.

At the end of the lesson, show programs that accomplish the same goal in a more readable, debuggable format. This is the via negativa approach to teaching: show students how bad a choice X is, so they are inspired to do not-X, i.e. the good practice. It will really hit him if they have to struggle through what happens when best practices aren't followed.

$\endgroup$
5
$\begingroup$

Use a language that has no jumps.

C, C++, Java, C# all have jumps (break, continue, return), though they have eliminated the most harmful jumps (even goto in C, you can not leave the subroutine). Most languages still have return.

Eiffel is a good object oriented language that has completely eliminated jumps, and many other harmful language features. At the same time as being easier to learn and use, it is more powerful that most other OO languages. (the down side is the lack of some special purpose libraries.)

$\endgroup$
  • $\begingroup$ Elegant expansion of what I currently do, I like it! :) $\endgroup$ – vacip Jun 9 '17 at 20:14
  • 1
    $\begingroup$ I have seen lint tools that will ban break, continue, and goto. However all the C languages need break for their broken switch/case. $\endgroup$ – ctrl-alt-delor Jun 10 '17 at 7:49
  • $\begingroup$ I figured out how to do fall-through Switch cases in C# using GoTo. I showed the students because I told them that it was an idiom that they might see in older production code. Re: "can't leave the subroutine" - what about setjmp() / longjmp() in C? Grok that and you really know something. $\endgroup$ – user737 Jun 15 '17 at 15:19
4
$\begingroup$

Fundamentally, this is a question of the level of abstraction and the most natural way to express something in a language. Modern high level languages generally have safer ways of expressing a concept than using a branch.

At the low level, assembler language is pretty dependant on branches. You can get some way with conditional execution as a way of hiding small branches, but one of the most critical elements of a processor's performance is it's branch predictor.

So there is a dilema, the conditional branch is a key part of control flow at the machine level - but easy to maintain and debug code will use control flow constructs which clearly express the designer's intent and are best placed for future scalability.

It should also be the case that relying more on the high-level control flow constructs gives a compiler more scope to deal well with a variety of different target architectures - this might be something you could link into showing how sometimes it is not just other humans reading your code that matter, occasionally a machine needs to be able to re-organise your code efficiently.

$\endgroup$
3
$\begingroup$

I would say to introduce jump statements as late as possible in the learning process. Start with a language like Eiffel that has no jump statements, then progress to others like Java and C, where their use is highly restricted. Then introduce a "case" where jump statements are widely used, and let them suffer through it.

Basically, you want to introduce students to an "innocent" period with no jump statements, and then later, to more sophisticated applications where such statements are used. Many students will then elect to retain their "innocence" (to the degree possible), while others will opt to "take their chances."

$\endgroup$
3
$\begingroup$

Teach them to use jump statements (but only within suitable contexts).

Jumps are perfectly normal in assembly language, machine code, and state machines (Turing machine advance tape by N). Teach jumps in languages that require them, and the alternatives in languages where there are more readable alternatives that avoid complexity and spaghetti issues in larger than trivial programs.

When teaching how their code is actually executed, show how the high-level structured control flow constructs are more readable versions that directly map to jump and conditional control flow jumps.

For advanced CS students, show how jumps can create a directed network where it is much harder to evaluate whether or not the network is even deterministic, much less what it does. Compared to loops and other structured constructs.

$\endgroup$
  • $\begingroup$ Right. You have to learn the rules before you can break them, you have to be able to write a structured solution before you can make a few exceptions within it. $\endgroup$ – user737 Jun 15 '17 at 15:27
3
$\begingroup$

Don't talk about them until far, far into the course.

By the time I found out about GOTOs (inadvertently, reading some algorithm in a paper) I was already very used to loops, and I was able to see an easy way to rewrite that part of the algorithm using a list comprehension (Python).

In the same way, don't introduce GOTOs in the course until your students are well able to use other techniques that can be used to rewrite code with GOTOs. Maybe spend some time rewriting code using GOTOs so they get used to that and the fact that there's nearly always a better way to do it.

Another option is to teach mainly using a language like Python, which doesn't have a built-in GOTO function.

$\endgroup$
3
$\begingroup$

I would challenge not teaching the correct use of "goto" as being a failure in the curriculum.

I learned programming in BASIC, and learned (eventually) to write clean structured code even though the only way to define flow was through "goto" and the nearly equally evil "gosub".

COBOL is still the most widely used language in industry. For those who think it's dead or dying, read this link: https://cis.hfcc.edu/faq/cobol

With 5 billion lines of code being added each year, it is still one of the most important programming languages in the industry ecosystem (as much as I hate to admit it). Due to the aging workforce, a COBOL programmer fresh out of college can jump into a $75K per year entry level position.

The primary flow control feature of COBOL is the "perform" statement. Since COBOL has no concept of scope or stack, everything is global to the program. A java programmer would be tempted to read

perform 0990-do-input through 0990-do-input-end.

as comparable to

doInput();

Unlike java, the paragraph defined between the tags 0990-do-input and 0990-do-input-end has no scope. The behavior of the perform is to execute a jmp to the paragraph, then a jmp back to the point after which it was called.

Since the "perform" statement is essentially a "goto", "structured programming" is still a very big deal for COBOL programmers, even though the rest of the world is working in languages that won't compile unless the code is "structured" far beyond the way that a COBOL programmer means it.

$\endgroup$
2
$\begingroup$

I'll add another (slightly) dissenting view. Years ago I read Eric Roberts' paper on the "loop-and-a-half" problem and was convinced by his evidence that students find it considerably easier to correctly write loops (especially indefinite sentinel loops) when allowed to exit from the middle of a loop (using a break in the case of Java or C++).

On the other hand, I also am careful to explain that break has to be handled with care, providing them with an article on the 1990 AT&T collapse, attributed to a misplaced break statement. I though of this article when I noticed a popular Java technical blog promoting "labeled breaks" to jump out of a nested if statement. I certainly avoid teaching my students things like that.

$\endgroup$
  • $\begingroup$ Thanks for the insight, and especially for the articles! $\endgroup$ – vacip Jul 19 '17 at 10:54
  • $\begingroup$ Another point is you definitely need break with range-based for loops (aka foreach), which are the simplest (and cleanest) way to visit a collection. $\endgroup$ – Michel Billaud Jul 19 '17 at 19:25
  • $\begingroup$ The paper forgets an aspect : assertions (Hoare et al.). With "while(i<n) { ....}", you can infer that i>=n holds after the loop, only if the body doesn't contain any break statement. If it does, it is more difficult to formally state what is going on there (but do students really care ?) $\endgroup$ – Michel Billaud Jul 19 '17 at 19:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.