Recent

Author Topic: how to break out early of a case ?  (Read 80192 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: how to break out early of a case ?
« Reply #180 on: December 20, 2023, 02:13:42 pm »
It wouldn't work if it were inside a procedure, which is what you were suggesting.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 5479
Re: how to break out early of a case ?
« Reply #181 on: December 20, 2023, 02:22:11 pm »
The trick is in the "while true" loop which repeats the case selection.
Exactly! :)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 17213
  • Ceterum censeo Trump esse delendam
Re: how to break out early of a case ?
« Reply #182 on: December 20, 2023, 02:24:49 pm »
Mark, there is also something like mode ISO or mode MACPAS that allow non-local goto. Although these modes have several other restrictions, those will exit too depending on how the code is written. On a lower level there is even SetJmp-LongJmp which basically provides you with another exit strategy.  The latter two are in system.

Both are no longer recommended of course, but they do exist.
More in general, if you deliberatly want to write bad code, use the archaic language features... O:-) 8-) Especially non-re-entrant ones..
« Last Edit: December 20, 2023, 02:33:51 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: how to break out early of a case ?
« Reply #183 on: December 20, 2023, 02:35:05 pm »
Mark, there is also something like mode ISO or mode MACPAS that allow non-local goto. Although these modes have several other restrictions, those will exit too depending on how the code is written. On a lower level there is even SetJmp-LongJmp which basically provides you with another exit strategy.  The latter two are in system.

Yes, I know. However I'm not promoting use of such things inside a procedure, but demonstrating that a bit of direct coding is sometimes by far the best way of doing something and that while "stylistically correct" Joanna's suggestion can be more trouble that it's worth.

...and before anybody says anything about Dijkstra and goto, I'd point out that he's later on record as writing that a quasi-religious aversion towards goto was not necessarily a pancea. Sometimes the best thing is to use the tools that a language gives you: carefully, and where appropriate documenting your rationale.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Joanna

  • Hero Member
  • *****
  • Posts: 1429
Re: how to break out early of a case ?
« Reply #184 on: December 21, 2023, 01:42:43 am »
This is thread is a good resource for anyone who ever wants to do this to reference  :)
« Last Edit: December 21, 2023, 01:52:37 am by Joanna »

dbannon

  • Hero Member
  • *****
  • Posts: 3388
    • tomboy-ng, a rewrite of the classic Tomboy
Re: how to break out early of a case ?
« Reply #185 on: December 21, 2023, 03:03:43 am »
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: how to break out early of a case ?
« Reply #186 on: December 21, 2023, 09:22:21 am »
Quote
"Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"

—Edsger Dijkstra

I believe there are one or two valid use cases, such as some older serial comms protocols which do not map easily to any of the layered models. Also consider this:

Code: Pascal  [Select][+][-]
  1.   case finalParam - (nextParam - 1) of
  2.     0: ;                                (* Use values etc. from config file     *)
  3.     1: begin
  4. valueOnly:
  5.          if finalParam >= nextParam then
  6.            values[address + offset] := parseValue(nextParam, values[address + offset])
  7.        end;
  8.     2: begin
  9. offsetAndValue:
  10.          if finalParam >= nextParam then
  11.            offset := parseOffset(nextParam);
  12.          nextParam += 1;
  13.          goto valueOnly                 (* Any sane language would allow a case *)
  14.        end                              (* selector to be treated as a label in *)
  15.   otherwise                             (* this context.                        *)
  16.     if finalParam >= nextParam then
  17.       address := parseAddress(nextParam);
  18.     nextParam += 1;
  19.     goto offsetAndValue
  20.   end;
  21.  

...in fact I find myself wondering whether the label keyword could be eliminated, with the only valid (albeit discouraged) use case of goto being in a case statement.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 5479
Re: how to break out early of a case ?
« Reply #187 on: December 21, 2023, 05:39:02 pm »
Just for the record...

I concede that the comment "to cause fall through as in C" found in the code could be misleading but, I thought it had already been clearly established in the thread that Pascal does _not_ offer any way of exiting a "case" early much less offer a "case" fall through feature.  IOW, it seems fine in the context in which it appears.

Zoran and Nitorami quickly figured out what how the whole thing works.  I mention this because I believe the construction is simple enough to be easily understood, which is important.

On a different note, generally speaking, I am dogmatic about the non-use of "goto".  I am convinced most modern programming languages, including FPC, provide enough flow control features to make the use of "goto" unnecessary in all cases.  This might not have been the case when Dijkstra made his comments about "goto"s.

For instance, using an FSM like the one I presented, allows a programmer "goto-ing" anywhere he/she wants - within the "case" - simply by altering the case control variable.  Moreover, using "break" and "continue" allows the creation of execution flows that would require genuine spaghetti code.  To be fair though, the manipulation (and potential abuse) of the case control variable should likely be considered a potential source of spaghetti code as well.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Joanna

  • Hero Member
  • *****
  • Posts: 1429
Re: how to break out early of a case ?
« Reply #188 on: December 22, 2023, 01:38:23 am »
Forgive me for a possible newbie questions..
Isn’t goto just a holdover from assembly language jump command ?
Also when using pascal without the goto, isn’t there still a goto being performed behind the scenes as part of the program flow?
« Last Edit: December 22, 2023, 01:42:28 am by Joanna »

440bx

  • Hero Member
  • *****
  • Posts: 5479
Re: how to break out early of a case ?
« Reply #189 on: December 22, 2023, 03:06:41 am »
Forgive me for a possible newbie questions..
No problem.  Asking questions are the simplest way of gathering knowledge, therefore, questions should be encouraged.

Isn’t goto just a holdover from assembly language jump command ?
No, not really.  The "goto" statement is a very general way of transferring control to someplace.  It is very common in Assembly language because Assembly, usually, doesn't provide for/while/repeat loops nor high level conditionals.  All of those must be synthesized using "goto"s or jumps of one kind or another (conditional, unconditional, indexed, etc.)

Also when using pascal without the goto, isn’t there still a goto being performed behind the scenes as part of the program flow?
Yes, there is definitely one or more "goto" (or jumps) being performed behind the scenes BUT, in spite of that, it is _very_ different than an explicit "goto" instruction.  The _crucial_ difference is that the target(s) of Pascal's "behind the scenes" jumps/gotos are pre-established.  For instance, when a programmer sees a "for" loop - which is implemented using gotos/jumps - the programmer knows the very instant he/she sees the "for" statement that there is an execution scope that is limited by the end of the "for" statement.  IOW, the "for" denotes an explicit execution boundary, the same is true of "while", "repeat", "if" and other instructions that are implemented using jumps/gotos.

Contrast that with seeing "goto somelabel" somewhere in the code.  the "somelabel" can be _anywhere_ (in Pascal, usually limited to the function/procedure in which it appears but, even that is not guaranteed.)  The fact that the label can be placed "anywhere" is what makes the "goto" totally undesirable.  It makes execution flow harder to visualize and follow.  Use more than one target label (which implies multiple gotos) and you've got code that desperately needs marinara sauce to be palatable.
 
The fact that the target of a goto can be anywhere IS what makes gotos unacceptable.

It should also be noted that "goto"s are not the only way of creating spaghetti code and, the _ultimate_ mechanism that enables creating spaghetti code is having the ability to change the target of the "goto".  Imagine there is a statement "goto label_1" and that somehow that statement can be modified at runtime to be "goto label_27" (instead of "label_1").  Now, not only the target (label_xxx) has to be located but, the real target can only be determined at runtime.  Most programmers can see the many problems that creates.  By the way, changing the target of a "goto" was/is a feature found in the COBOL language using the instruction "ALTER".  Fortunately, companies decided that any programmer who used "ALTER" had to be _fired_ (and, yes, that is an excellent reason to fire a programmer.) 

Amazingly, the runtime dynamic target "feature" has become a pervasive element in modern programming and, that's all I'm going to say about that. 

There is no room nor need for "goto" in any correct program written in Pascal. 
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: how to break out early of a case ?
« Reply #190 on: December 22, 2023, 09:36:33 am »
Goto exists in various forms in all types of language. although proponents of some would deny it ** .

Pascal's predecessors: ALGOL, COBOL and of course FORTRAN all had different forms of goto, Wirth tidied things up enormously.

However Pascal- even pre-objects- still has procedure variables which are basically pointers to functions, and we have to trust the compiler to not (allow the user to) foul things up. Everything here is based on the strength of the compiler's type checking: in principle it will never allow control to be transferred to the wrong type of procedure (i.e. expecting the wrong number of parameters etc.) which is one reason why nil- compatible with any pointer- is considered bad news.

Wirth's early code- his doctorate through to ALGOL-W- was, generally speaking, a mess: he learnt the benefit of carefully-managed control and data structures the hard way.

Something that you will see on occasion is the claim that such-and-such a well-defined structure allows software to /reason/ about the intention of a body of source code: I think that's putting things somewhat floridly, but at the same time from the code generation POV there are definite benefits to being able to say e.g. "we're now at point C, control must have arrived here from either point A or from point B hence we know exactly what registers are being used for temporary variables".

I'd take this opportunity to criticise the exposed form variable of Delphi and the LCL. That should be protected in some way so that once set up it can never be corrupted.

** Specifically, proponents of "functional" languages would deny it, on the grounds that "returning a function" which has been created on the fly is not a goto. I'm unconvinced, since that's basically a "pointer to a procedure" as discussed above so everything again boils down to type checking.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Josh

  • Hero Member
  • *****
  • Posts: 1375
Re: how to break out early of a case ?
« Reply #191 on: December 22, 2023, 11:09:10 am »
Hi

Not read whole thread full of the flu, but this is what i have done.

Probably not suitable or similar code already posted, but hey ho, no ho ho ho...

Code: Pascal  [Select][+][-]
  1. Var ControlVar:Integer;
  2.  
  3. function MyControlLogic(Var aValue:Integer):Boolean;
  4. begin
  5.   result:=False;
  6.   Case aValue of
  7.     1:Begin
  8.         // do Stuff
  9.        aValue:=3;
  10.        WriteLn('1 become 3');
  11.       end;
  12.     2:Begin
  13.         // do Stuff
  14.        WriteLn('At 2 Exiting');
  15.        Exit(True);
  16.       end;
  17.     3:Begin
  18.         // do Stuff
  19.        aValue:=2;
  20.        WriteLn('3 become 2');
  21.       end;
  22.     4:begin
  23.         // do Stuff
  24.         WriteLn('A One off 4');
  25.         Exit(True);
  26.       end
  27.   else
  28.     Begin
  29.       WriteLn('A Drop through');
  30.       Exit(True);
  31.     End;
  32.   end;
  33. end;
  34.  
  35. begin
  36.   ControlVar:=1;
  37.   Repeat Until MyControlLogic(ControlVar);
  38.  
  39.   ControlVar:=4;
  40.   Repeat Until MyControlLogic(ControlVar);
  41.  
  42.   ControlVar:=5;
  43.   Repeat Until MyControlLogic(ControlVar);
  44.  
  45. end.          
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Joanna

  • Hero Member
  • *****
  • Posts: 1429
Re: how to break out early of a case ?
« Reply #192 on: December 22, 2023, 11:27:26 am »
Thanks for the clarification about the goto. My first two programming languages {Basic & FORTRAN } used numbered lines and required goto for  just about everything. When I got to pascal I was told that pascal has a goto but never to use it because it wasn’t necessary.  It was quite a surprise at the time that programming could be done without a goto...

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: how to break out early of a case ?
« Reply #193 on: December 22, 2023, 01:03:09 pm »
Thanks for the clarification about the goto. My first two programming languages {Basic & FORTRAN } used numbered lines and required goto for  just about everything. When I got to pascal I was told that pascal has a goto but never to use it because it wasn’t necessary.  It was quite a surprise at the time that programming could be done without a goto...

BTDT, I can identify. However even before people started to consider that ALGOL-based languages might possibly be some use for application or even systems programming it was extremely common either for assemblers to come with predefined BEGIN and END constructs (etc.) or for users to define them as macros... and there are still mainframe programmers who insist that IBM Macro Assembler is the pinnacle of intellectual endeavour.

Let's look at some of the implications. If you have a simple WHILE fetz DO BEGIN ... END the compiler will build a branch on its internal Abstract Syntax Tree (AST) representing that entire structure, which at some later point will be converted to something executable by hardware. If instead you have WHILE FALSE DO BEGIN END, the compiler has the option of deleting the branch from the AST in its entirety... /provided/ that it's not possible to jump into it by some other route.

From a different angle, I was once at a conference where the paper following mine criticised BREAK/EXIT/CONTINUE as closet GOTOs: not so much because they make the code generator's job harder (which they do) but because Dijkstra was considered to be dead against GOTO in all its forms. At which point all I can do is repeat the quote from earlier:

Quote
"Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"

—Edsger Dijkstra

There are cases where it is impossible to deny that a blanket prohibition on GOTO is beneficial. For example, if a nested BEGIN...END could, at the compiler's discretion, result in the creation of a local stackframe then a complete ban on being able to jump into or out of it is valuable.

On the other hand, there are cases where the maths and in particular array-processing crowd attempts to promote the avoidance of control transfer ("GOTO and all his works") to an extent that would make them figures of fun if they weren't so deathly serious. For example, I have seen code not unlike the equations at https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_real_or_complex_numbers where the writer has assumed that the "purity" of an array-based computation would be so sullied by a simple iteration to decide which of two possibilities should be used for each element that he has introduced transcendals in an attempt to simulate a discontinuous operator.

The gripping hand is that sometimes suboptimal control transfer (i.e. break etc.) is justified, and on very rare occasions so is goto. But it shouldn't be used casually.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

munair

  • Hero Member
  • *****
  • Posts: 884
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: how to break out early of a case ?
« Reply #194 on: May 24, 2025, 11:41:21 pm »
Since this thread some years ago, I've revisited the implementation in SharpBASIC, which was based on 440bx's idea to force explicit fall through (e.g. with a statement like 'fall'). as opposed to C's implicit fall through. But how should it behave? Should it ignore the next branch's condition(s) entirely? What if you do want the next branch to be tested, despite the current branch already being a match? More control is possible, using multiple directives, like 'fall', 'next' and 'done':

Code: [Select]
' SharpBASIC switch statement directives example
' ----------------------------------------------
dim n: int = 5;

main do
  by n
    is 1 to 5 do
      print("1 to 5");
      if n > 3 do
        n = 7;
        next; ' jumps to next branch and tests condition
      elseif n == 1 do
        done; ' leaves switch statement
      end
      fall; ' falls into next branch and ignores condition
    is 6 to 10 do
      print("6 to 10");
      if n == 7 do
        fall;
      end
      next;
    is 11 to 15 do
      print("11 to 15");
      if n == 7 do
        n = 15;
        next;
      end
    is any do
      print("any value");
  end
end

This allows for full control of the switch statement. Not sure if there are many use cases for these examples, but the flexibility is there.  ;)
« Last Edit: May 24, 2025, 11:45:36 pm by munair »
It's only logical.

 

TinyPortal © 2005-2018