Author Topic: An improved FOR NEXT loop  (Read 6610 times)

0 Members and 1 Guest are viewing this topic.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
An improved FOR NEXT loop
« on: July 10, 2021, 03:44:50 am »
The usual FOR NEXT loops don't always work as expected.
FOR NEXT in QBASIC and QB45 too.

FOR X = 0 to 1
'CODE
should execute code only once - it does

FOR X = 0 to 0
'CODE
should execute code only zero times - it executes once

does not look consistent!
  Also the counter variable stays incremented after the FOR NEXT exits. So If A = 255,   B = 256 after the exit.  There are many times when this has caused hard to trace bugs.

Quote
DIM A AS _UNSIGNED _BYTE
A = 255

DIM B AS INTEGER


FOR B = 0 TO A

    PRINT "hI"

NEXT B

PRINT B


'A better FOR NEXT work around
DO
    IF A = 0 THEN EXIT DO

    '***** code *****

    IF B = A THEN EXIT DO
    B = B + 1

LOOP
PRINT B  ' = 255  exactly as you'd expect.







« Last Edit: July 10, 2021, 03:57:22 am by NOVARSEG »

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: An improved FOR NEXT loop
« Reply #1 on: July 10, 2021, 06:36:16 am »
Quote
FOR X = 0 to 1
'CODE
should execute code only once - it does
No, it executes twice.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: An improved FOR NEXT loop
« Reply #2 on: July 10, 2021, 07:53:03 am »
Do you have a specific example of code that gives the described effect?

I tried the following simple bit and had no issues:
Code: QB64: [Select]
  1. FOR x = 0 TO 1
  2.     PRINT x
  3. FOR y = 0 TO 0
  4.     PRINT y
  5.  

The output it gave me was just as expected:
0
1


0

Otherwise, I'd agree. If you're going to use a variable after a FOR...NEXT loop, using the loop variable itself can sometimes cause issues. I usually reserve a few throw away variables as loop counters, often just recycling the same one. Then if I'm looking for some met condition in the loop code, I'll just increment some other variable as a counter.

I generally find DO...LOOP and FOR...NEXT to be nearly interchangeable, and I tend to go for DOs more often, as I've heard it said that they are somewhat faster. I try to see what gets the job done in the least amount of code, using the philosophy that the best and most reliable part is no part.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: An improved FOR NEXT loop
« Reply #3 on: July 10, 2021, 08:23:38 am »
To round out the discussion here is a no use loop:
Code: QB64: [Select]
  1. ' a no use loop
  2.  
  3. For i = 0 To -1
  4.     Print "You wont see this."
  5. Print "And check i after loop ="; i
  6.  

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: An improved FOR NEXT loop
« Reply #4 on: July 10, 2021, 08:38:42 am »
@NOVARSEG

Hi
I think that you read different from me the instruction FOR NEXT
so you expected a different behaviour.

My vision is 
Quote
from Start (included) to End (included) Step (default 1)
   do this
next (again)
while it seems that your vision is
Quote
from Start (excluded) to End (included) step (default 1)
     do this
next (again)

or in a different way
you think that the counter of loop increases before to execute the instructions inner to the loop
Quote
from Start to End Step (default 1)
it increases counter here and then it tests if is in the range Start End
 do this
next (again)

here 2 alternative loop emulating FOR NEXT to see how the starter value of Start is in the range of option to execute the instructions into the loop

Code: QB64: [Select]
  1. 'alternative way to emulate FOR NEXT loop
  2.  
  3.  
  4. ' FOR NEXT syntax taken from QB64 wiki
  5. 'Description
  6. 'FOR...NEXT counter loops must be within the proper start, stop and
  7. 'increment values or the entire loop code block will not be executed.
  8. 'Avoid changing the FOR counterVariable's value inside of the loop.
  9. ' This obfuscates code and is a poor programming practice.
  10. 'Once the loop has been started, changing the variables holding
  11. ' the startValue, stopValue or increment value will not affect loop execution.
  12. 'If the STEP increment value does not match the startValue TO
  13. 'stopValue the FOR loop block will be ignored.
  14. 'If startValue is less than stopValue, use the default increment
  15. 'or positive STEP value or the loop will not be executed.
  16. 'If startValue is more than stopValue, use a negative STEP interval
  17. 'or the loop will not be executed.
  18. 'The STEP increment value cannot be changed inside of the loop.
  19. 'Use EXIT FOR to leave a FOR loop early when a certain condition
  20. 'is met inside of the loop.
  21. 'Use _CONTINUE to skip the remaining lines in the current iteration
  22. 'of a FOR/NEXT block without leaving the loop.
  23. 'The NEXT counter variable name is not required.
  24. 'NEXT loop increments can be separated by colons in nested FOR loops.
  25. 'NOTE: When the FOR loop is exited after the stopValue is reached,
  26. ' the counterVariables value will be stopValue + 1
  27. '(or stopValue + increment)
  28. 'Beware of FOR loop counts that exceed the counterVariable type
  29. 'limits and may repeat without error in QB64.
  30. 'For example, if counterVariable is of type INTEGER and the stop
  31. 'limit exceeds 32767, the counterVariable will reset back to -32768
  32. ' and loop endlessly.
  33.  
  34. 'Qbasic syntax
  35. 'FOR...NEXT Statement
  36. 'Repeats a block of statements a specified number of times.
  37. 'counter A numeric variable used as the loop counter.
  38. 'start and end   The initial and final values of the counter.
  39. 'increment   The amount the counter is changed each time through the loop.
  40.  
  41.  
  42. Print "Min = 0, Max = 1"
  43. min = 0
  44. max = 1
  45. steps = 1
  46. whochange = 999
  47. ForNext1 min, max, steps, whochange
  48.  
  49. min = 0
  50. max = 1
  51. steps = 1
  52. whochange = 999
  53. ForNext2 min, max, steps, whochange
  54.  
  55. Print "Min = 0, Max = 0"
  56.  
  57. min = 0
  58. max = 0
  59. steps = 1
  60. whochange = 999
  61. ForNext1 min, max, steps, whochange
  62.  
  63. min = 0
  64. max = 0
  65. steps = 1
  66. whochange = 999
  67. ForNext2 min, max, steps, whochange
  68.  
  69.  
  70. Sub ForNext1 (min As Integer, max As Integer, steps As Integer, whoChange As Integer)
  71.     ' emulating For Next using Label + GOTO
  72.     whoChange = min
  73.     n = 0
  74.     NextAgain:
  75.     n = n + 1
  76.     Print "it runs "; n; " times"
  77.     whoChange = whoChange + steps
  78.     If whoChange >= min And whoChange <= max GoTo NextAgain
  79.  
  80. Sub ForNext2 (min As Integer, max As Integer, steps As Integer, whoChange As Integer)
  81.     ' emulating For Next using DO LOOP
  82.     whoChange = min
  83.     n = 0
  84.     Do While whoChange <= max And whoChange >= min
  85.         n = n + 1
  86.         Print "it runs "; n; " times"
  87.         whoChange = whoChange + steps
  88.     Loop

Copy, paste and run.
Programming isn't difficult, only it's  consuming time and coffee

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: An improved FOR NEXT loop
« Reply #5 on: July 10, 2021, 10:20:17 am »
Seems to me it works exactly as expected
Shuwatch!

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: An improved FOR NEXT loop
« Reply #6 on: July 10, 2021, 01:12:52 pm »
A little off topic but it might be handy to have a NEXTIF

For x = 1 to 10
Input "Are you still a happy soul": Ans$

NextIf Ans$="Yes"

of course we do have Exit For and even the Do Loop Until stuff.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: An improved FOR NEXT loop
« Reply #7 on: July 10, 2021, 10:01:10 pm »
OK  the code executes twice (my mistake) but B will have a value of 2 after the exit.


Quote

DIM A AS _UNSIGNED _BYTE
A = 1

DIM B AS INTEGER


FOR B = 0 TO A

    PRINT "hI"

NEXT B

PRINT B

The problem of B = 2 after the exit does not seem correct.

This is the fix
Quote
B = 0
DO
    PRINT "hi2"

    IF B = A THEN EXIT DO
    B = B + 1

LOOP
PRINT B ' = 1  exactly as you'd expect.

This is not a bug as it exists in many versions of  BASIC. However the programmer should be aware of the exit value of the counter in a FOR NEXT loop

« Last Edit: July 10, 2021, 10:12:45 pm by NOVARSEG »

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: An improved FOR NEXT loop
« Reply #8 on: July 10, 2021, 10:46:50 pm »
introduction of spaghetti code is never an improvement or better.
Granted after becoming radioactive I only have a half-life!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: An improved FOR NEXT loop
« Reply #9 on: July 11, 2021, 12:12:18 am »
introduction of spaghetti code is never an improvement or better.

Not see'n where that's ("spaghetti code") coming from???
« Last Edit: July 11, 2021, 12:14:05 am by bplus »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: An improved FOR NEXT loop
« Reply #10 on: July 11, 2021, 01:42:25 am »
OK so this is the action of the usual FOR NEXT loop

notice that things go wrong when B is a _UNSIGNED _BYTE

B has a value of 255 and then is incremented and so now equals zero. The FOR NEXT goes into an endless loop but as we know that does not happen - confused?

Hit ESCAPE key if you run the code

Quote
DIM A AS _UNSIGNED _BYTE
A = 255

'DIM B AS INTEGER
DIM B AS _UNSIGNED _BYTE

B = 0
DO

    PRINT "hi"
    B = B + 1
    IF B > A THEN EXIT DO

    IF INKEY$ = CHR$(27) THEN EXIT DO

LOOP
PRINT B

END

I suppose whoever coded the original FOR NEXT loop noticed that wrap around, so had to add extra code to prevent the endless loop.

Quote
DIM A AS _UNSIGNED _BYTE
A = 255

'DIM B AS INTEGER
DIM B AS _UNSIGNED _BYTE

B = 0
DO

    PRINT "hi"
    B = B + 1
   if B = 0 then EXIT DO ' or maybe assembler instruction LOOPNZ etc
IF B > A THEN EXIT DO

   LOOP
PRINT B

END

However, the following code has only one IF condition and after exit the B counter has a value that matches A

Quote
DIM A AS _UNSIGNED _BYTE
A = 255

'DIM B AS INTEGER
DIM B AS _UNSIGNED _BYTE
B = 0
DO
    PRINT "hi2"

    IF B = A THEN EXIT DO
    B = B + 1

LOOP
PRINT B ' = 255  exactly as you'd expect.



« Last Edit: July 11, 2021, 02:07:12 am by NOVARSEG »

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: An improved FOR NEXT loop
« Reply #11 on: July 11, 2021, 02:33:26 am »
Not see'n where that's ("spaghetti code") coming from???

Code: QB64: [Select]
  1.     IF A = 0 THEN EXIT DO
  2.  
  3.     '***** code *****
  4.  
  5.     IF B = A THEN EXIT DO
  6.     B = B + 1
  7.  
  8.  

spaghetti code, kind of hard to miss man.
Granted after becoming radioactive I only have a half-life!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: An improved FOR NEXT loop
« Reply #12 on: July 11, 2021, 03:27:07 am »
@Cobalt
 I made some mistakes in the code but corrected them.  FOR NEXT loops work well enough.   FOR NEXT loops must have been coded 50 years ago and they work fine but the counter variable, after exit,  is always one more than the limit (or 0 in the case of a wrap around) and it does not have to be that way.

I can't think of any good reason for it.


« Last Edit: July 11, 2021, 03:28:35 am by NOVARSEG »

Offline euklides

  • Forum Regular
  • Posts: 128
    • View Profile
Re: An improved FOR NEXT loop
« Reply #13 on: July 11, 2021, 04:33:26 am »
 It's so basic....

'Dont forget that the for/next command stops when x becomes > ITERATION

START = 0
ITERATION = 10
County = 0

For x = START To ITERATION
    County = County + 1
Next x
Print "County="; County; " and X="; x; "    Calculated steps for next here is :"; (ITERATION - START + 1); ""
Why not yes ?

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: An improved FOR NEXT loop
« Reply #14 on: July 11, 2021, 08:01:55 am »
spaghetti code, kind of hard to miss man.

Not a single GOSUB let alone a GOTO must be talk'n from Cobalt Code World again.

If you are dissing EXIT DO again that saves us from spaghetti code, at least in bplus Code World ;-))

EXIT loop is an elegant way out of a loop. I don't think that's just my opinion, I think the majority of people who complained of spaghetti code would agree that it sure beats a GOTO out of loop.
« Last Edit: July 11, 2021, 08:07:39 am by bplus »