Author Topic: Select Case  (Read 6732 times)

0 Members and 1 Guest are viewing this topic.

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: Select Case
« Reply #15 on: January 22, 2022, 08:29:14 pm »
Yup, just for the giggles.

Kind of like stacking dolls not quite sure what they are doing ...

Code: QB64: [Select]
  1. get_values:
  2.         input "Event Number: "; Event
  3.         input "Temperature: "; Temperature
  4.         input "SnowDays: "; SnowDays
  5.         input "Windspeed: "; Windspeed
  6. '
  7. on Event gosub Event1, Event2, Event3, Event4
  8. goto get_values
  9. '
  10. Event1:
  11.         Count1 = Temperature
  12.         if Count1 < 0 then
  13.                 gosub Event2
  14.    else
  15.                 print "RainEvent = RainEvent + 1"
  16.         end if
  17.         return
  18. Event2:
  19.         Count2 = SnowDays
  20.         if Count2 > 31 then
  21.                 gosub Event3
  22.         else
  23.                 print "SnowEvent = SnowEvent + 1"
  24.         end if
  25.         return
  26. Event3:
  27.         Count3 = Windspeed
  28.         if Count3 > 100 then
  29.                 gosub Event4
  30.         else
  31.                 print "WindEvent = WindEvent + 1"
  32.         end if
  33.         return
  34. Event4:
  35.         print "HurricaneEvent = HurricaneEvent + 1"
  36.         return]
  37. get_values:
  38.         input "Event Number: "; Event
  39.         input "Temperature: "; Temperature
  40.         input "SnowDays: "; SnowDays
  41.         input "Windspeed: "; Windspeed
  42. '
  43. on Event gosub Event1, Event2, Event3, Event4
  44. goto get_values
  45. '
  46. Event1:
  47.         Count1 = Temperature
  48.         if Count1 < 0 then
  49.                 gosub Event2
  50.    else
  51.                 print "RainEvent = RainEvent + 1"
  52.         end if
  53.         return
  54. Event2:
  55.         Count2 = SnowDays
  56.         if Count2 > 31 then
  57.                 gosub Event3
  58.         else
  59.                 print "SnowEvent = SnowEvent + 1"
  60.         end if
  61.         return
  62. Event3:
  63.         Count3 = Windspeed
  64.         if Count3 > 100 then
  65.                 gosub Event4
  66.         else
  67.                 print "WindEvent = WindEvent + 1"
  68.         end if
  69.         return
  70. Event4:
  71.         print "HurricaneEvent = HurricaneEvent + 1"
  72.         return
  73.  
  74.  

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Select Case
« Reply #16 on: January 22, 2022, 08:31:40 pm »
Yeah, GOTO is usually okay if you're branching forward and not out of control blocks.

For example, you should never do this:

Code: QB64: [Select]
  1. FOR I = 1 TO 10  
  2.    GOTO Done
  3. Done:

Honestly, I don't see anything wrong with what you've got here.  (Though I imagine that GOTO would probably be in an IF condition or such instead of just stand-alone.)

With a GOTO out of your FOR loop, you preserve the value of I, which might be the whole point of your loop.

Example:

Code: [Select]
DIM WordList(1000000) AS STRING

FOR I = 1 TO 1000000
   IF WordList(I) = UserWord$ GOTO Word_Found
NEXT

Word_Found:

IF I < 1000001 THEN
   PRINT "Your word was found at position #"; I
ELSE
   PRINT "Word not found."
END IF

I can't think of a much simpler way to do the above than this, and it's easy to understand what's going on with it.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline tomxp411

  • Newbie
  • Posts: 28
    • View Profile
Re: Select Case
« Reply #17 on: January 22, 2022, 08:37:15 pm »
Wow. Now that's a really good classic BASIC example. I can almost see the line numbers on it. =)

Now we can remove the GOTOs and simplify it even more:

Code: QB64: [Select]
  1. get_values:
  2.    input "Event Number: "; Event
  3.    input "Temperature: "; Temperature
  4.    input "SnowDays: "; SnowDays
  5.    input "Windspeed: "; Windspeed
  6. '
  7. on Event gosub Event1, Event2, Event3, Event4
  8. goto get_values
  9. '
  10. Event1:
  11.    Count1 = Temperature
  12.    if Count1 < 0 then goto Event2
  13.    print "RainEvent = RainEvent + 1"
  14.    return
  15. Event2:
  16.    Count2 = SnowDays
  17.    if Count2 > 31 then goto Event3
  18.    print "SnowEvent = SnowEvent + 1"
  19.    return
  20. Event3:
  21.    Count3 = Windspeed
  22.    if Count3 > 100 then goto Event4
  23.    print "WindEvent = WindEvent + 1"
  24.    return
  25. Event4:
  26.    print "HurricaneEvent = HurricaneEvent + 1"
  27.    return
  28.  

ON GOTO/ON GOSUB is one of my favorite tools in the MS BASIC toolbox, and it can be a good alternative to SELECT CASE for line-number BASIC when your domain is limited to a small, contiguous range of integer values. (Menus, for example, are a perfect place to use this.)

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: Select Case
« Reply #18 on: January 22, 2022, 08:43:55 pm »

Code: [Select]
DIM WordList(1000000) AS STRING

FOR I = 1 TO 1000000
   IF WordList(I) = UserWord$ GOTO Word_Found
NEXT

Word_Found:

IF I < 1000001 THEN
   PRINT "Your word was found at position #"; I
ELSE
   PRINT "Word not found."
END IF

I can't think of a much simpler way to do the above than this, and it's easy to understand what's going on with it.

That is a really nice code sample.  I nominate that as poster child of when the "evil" GOTO can be used for good. 👍

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: Select Case
« Reply #19 on: January 22, 2022, 08:52:14 pm »

Code: QB64: [Select]
  1. get_values:
  2.    input "Event Number: "; Event
  3.    input "Temperature: "; Temperature
  4.    input "SnowDays: "; SnowDays
  5.    input "Windspeed: "; Windspeed
  6. '
  7. on Event gosub Event1, Event2, Event3, Event4
  8. goto get_values
  9. '
  10. Event1:
  11.    Count1 = Temperature
  12.    if Count1 < 0 then goto Event2
  13.    print "RainEvent = RainEvent + 1"
  14.    return
  15. Event2:
  16.    Count2 = SnowDays
  17.    if Count2 > 31 then goto Event3
  18.    print "SnowEvent = SnowEvent + 1"
  19.    return
  20. Event3:
  21.    Count3 = Windspeed
  22.    if Count3 > 100 then goto Event4
  23.    print "WindEvent = WindEvent + 1"
  24.    return
  25. Event4:
  26.    print "HurricaneEvent = HurricaneEvent + 1"
  27.    return
  28.  

ON GOTO/ON GOSUB is one of my favorite tools in the MS BASIC toolbox, and it can be a good alternative to SELECT CASE for line-number BASIC when your domain is limited to a small, contiguous range of integer values. (Menus, for example, are a perfect place to use this.)

That just makes me giddy.  Way more fun than I expected for this Saturday night.  If I smoked pot, I'm pretty sure I'd be celebrating with a doobie right now.

This all totally takes me back to when I had all of 3.5KB for my BASIC programs.

Offline tomxp411

  • Newbie
  • Posts: 28
    • View Profile
Re: Select Case
« Reply #20 on: January 22, 2022, 08:52:24 pm »
Honestly, I don't see anything wrong with what you've got here.  (Though I imagine that GOTO would probably be in an IF condition or such instead of just stand-alone.)

It causes memory leaks, specifically in the stack. Since the NEXT doesn't get executed, the FOR's spot on the stack doesn't get removed, causing the stack to be pointing at the wrong place after exiting the loop. In extreme cases, this could cause errors further down the program, such as RETURN going to the wrong place or just running out of stack space if that loop gets repeated often.

Also, if this loop is contained inside a larger loop, this could cause the NEXT to advance the wrong loop. Consider this example

For I = 1 To 10
    For J = 1 To 10
        GoTo AfterJ
    Next
    AfterJ:
Next
Print I, J

On some BASICs, this actually prints 1,10. This is because the second NEXT actually advances J, rather than I. This is because BASIC doesn't know or care which FOR is being advanced. It just iterates the first loop in the stack and goes back to that line.

QB64 actually does this correctly (and presumably unwinds the stack on the GOTO out), but it's still unwise policy.


If you do need to exit a loop early, you should use the EXIT FOR command, which should properly unwind the stack.

Code: QB64: [Select]
  1. FOR I=1 TO 10
  2.   IF exit_condition THEN EXIT FOR
  3.   ...
  4.  

Also, in some languages, the loop counter (I) also goes out of scope after the loop completes, so it's never wise to count on a loop control variable's state after a loop. In fact, some compilers (I think c# does this) will actually throw an error when attempting to reference a control variable after a loop has completed.

If you do need to preserve the counter when you reach an early exit state, you can save it off to another variable. But there's another important consideration: FOR  actually the wrong construct to use when you may need to bail early. If you're doing something like searching a list or checking a range of values, that's actually what a WHILE loop is for.

Take this example of a linear search:


FOR I = 1 TO 100
  IF Domain(I) = Match THEN EXIT FOR
NEXT

This is actually better done as a WHILE loop, since WHILE signals that we may be exiting early:

I = 1
WHILE Domain(i) <> Match AND I <= UBOUND(Domain)
  I = I + 1
WEND

Either of these examples will work, but the latter example is more correct. This is because the thing we are looking for (Match) is canonically part of the loop condition, rather than being a line inside the loop somewhere.
« Last Edit: January 22, 2022, 09:03:28 pm by tomxp411 »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Select Case
« Reply #21 on: January 22, 2022, 09:23:29 pm »
Quote
It causes memory leaks, specifically in the stack.

FOR isn't a command which puts anything on the stack.  Basically, a FOR... NEXT loop translates into something like the following:


FOR I = 1 TO 10
   PRINT I
NEXT

Translates to: (a simplified version here, for illustration)

I = 1
I_Start:
PRINT I
I =I + 1
IF I <= 10 THEN GOTO I_Start

At the core level, a FOR loop is nothing but a self-contained GOTO loop.  Using GOTO to exit it won't harm your program at all, nor affect any stack space.

Stack space mainly comes into effect when dealing with subs/functions and gosubs.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline tomxp411

  • Newbie
  • Posts: 28
    • View Profile
Re: Select Case
« Reply #22 on: January 22, 2022, 09:32:11 pm »
FOR isn't a command which puts anything on the stack.  Basically, a FOR... NEXT loop translates into something like the following:


FOR I = 1 TO 10
   PRINT I
NEXT

Translates to: (a simplified version here, for illustration)

I = 1
I_Start:
PRINT I
I =I + 1
IF I <= 10 THEN GOTO I_Start

At the core level, a FOR loop is nothing but a self-contained GOTO loop.  Using GOTO to exit it won't harm your program at all, nor affect any stack space.

Stack space mainly comes into effect when dealing with subs/functions and gosubs.

Yes, I was thinking more of classic BASIC implementations, where GOTO out of a loop leaves the wrong return address. MS BASIC does use a stack to store FOR addresses, and using GOTO out of a loop then causes the next NEXT statement to jump back to the wrong FOR statement.

Interestingly, try something like this on a Commodore 64:
10 FOR X=1 TO 100
20 GOTO 50
30 NEXT
50 GOTO 10

It does not actually cause a stack overflow, but if you wrap that inside of another FOR loop, the subsequent NEXT will advance the wrong loop counter.

QB64 does do this the way you'd expect (NEXT statements jump back to the correct FOR statement), but I'd hate to see someone learn to do this in QB64, work in another language or on another system, and then find their code breaks in unexpected ways because they relied on this behavior.

I figure if this is a learning experience for the OP, we might as well teach them the best way that will serve them well down the road. =)
« Last Edit: January 22, 2022, 09:44:21 pm by tomxp411 »

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: Select Case
« Reply #23 on: January 23, 2022, 09:36:51 am »
Thank you gang .... you have expanded my thinking once again