Strange nested loop behaviour
Post by Richard Russell on Oct 8th, 2015, 5:55pm
Try running this code in LBB and in LB/JB:
Code: for y = 1 to 3
for x = 1 to 5
if x = 4 then goto [skip]
print x, y
next x
[skip]
next y
In LBB the output is what you would expect:
Code:1 1
2 1
3 1
1 2
2 2
3 2
1 3
2 3
3 3
But LB 4.04, LB 4.5.0 and JB all produce this very strange result:
Code:1 1
2 1
3 1
5 1
1 2
2 2
3 2
5 2
1 3
2 3
3 3
5 3
How does x ever get to have the value 5? I know that jumping out of a loop is bad practice but in this case the effect seems to be much more peculiar than just a memory leak!
Richard.
Re: Strange nested loop behaviour
Post by SarmedNafi on Oct 8th, 2015, 7:31pm
The correct code for LB must be like this:
Code:
for y = 1 to 3
for x = 1 to 5
if x = 4 then exit for
print x, y
next x
next y
That was documented, of how to exit from a loop.
Re: Strange nested loop behaviour
Post by SarmedNafi on Oct 8th, 2015, 7:35pm
Now we know a new feature of LBB.
We can jump out of a loop using a branch label safety.
Re: Strange nested loop behaviour
Post by Richard Russell on Oct 8th, 2015, 8:11pm
on Oct 8th, 2015, 7:31pm, SarmedNafi wrote:The correct code for LB must be like this |
|
It is well known that jumping out of a loop causes a memory leak (and it does in LBB too) but that doesn't explain the peculiar behaviour I highlighted. A memory leak isn't necessarily 'fatal' so although undesirable there are circumstances when it could be acceptable (see below). The code ought to work 'properly' despite the leak, but in LB/JB it doesn't.
Quote:Now we know a new feature of LBB. We can jump out of a loop using a branch label safety. |
|
What makes you think that? Jumping out of a loop in LBB causes a memory leak, so can - if it happens enough times - eventually cause memory to be exhausted (and even if that doesn't happen may cause performance to be impaired).
However in the specific case of the program I listed there is no net memory leak in LBB, because the memory leaked by jumping out of the inner (x) loop is reclaimed by the next y statement in the outer loop. In other words, NEXT 'pops' inner loops if necessary until the loop variable matches.
This means that if you have some legacy code which jumps out of a FOR loop, and if for some reason it is too difficult to correct it 'properly', you could eliminate the leak by containing the whole thing in a dummy loop as follows:
Code: for dummy = 1 to 1
' code which contains 'leaky' FOR loops
next dummy ' reclaims leaked memory
But there isn't an equivalent fix for jumping out of a while or do loop. Using EXIT is always the proper way.
Richard.
Re: Strange nested loop behaviour
Post by SarmedNafi on Oct 8th, 2015, 9:45pm
Thank you Richard.
Re: Strange nested loop behaviour
Post by RNBW on Oct 9th, 2015, 11:36am
I have run the code in QB64 and it comes up with the same result as LBB.
It isn't good practice to jump out of a for next loop, but if the interpreter/compiler allows you to do it then it should come up with the correct answer. If it is incorrect to do something it shouldn't let you do it and should come up with an error message, stopping the code from being run further.
GOTOs are now considered bad practice, but using them is not wrong and the code using them will come up with the correct answer.
LBB is not doing anything strange.
Re: Strange nested loop behaviour
Post by Richard Russell on Oct 9th, 2015, 3:03pm
on Oct 9th, 2015, 11:36am, RNBW wrote:If it is incorrect to do something it shouldn't let you do it and should come up with an error message, stopping the code from being run further. |
|
Sadly you are asking for the impossible. In a compiled language which parses the source code as part of the compilation process it is more practical to expect the language to report an error or a warning if you do something which you shouldn't. But an interpreted language such as Liberty BASIC or BBC BASIC cannot do that.
For example it is perfectly legitimate to jump out of a loop if you jump back in again! It may result in 'spaghetti code' but the interpreter doesn't realise that. So the interpreter cannot generate an error if you jump out of a loop because it has no idea whether you will jump back in again (which is OK) or not jump back in again but subsequently execute a NEXT for an outer loop (which is OK) or cause a memory leak (which is not OK). You might even do different things according to the result of a conditional test!
Since the interpreter cannot predict the future (it is not alone!) it can't report an error if you do something which may be OK or may not be OK depending on what you do later. Generally, an interpreted language will report an error at run time only if it tries to do something which is impossible (such as dividing by zero).
Richard.
Re: Strange nested loop behaviour
Post by tsh73 on Oct 9th, 2015, 5:05pm
My guess that if last loop instruction executed was "for x=..." and somehow interpreter hit line "next y", it has information that something went wrong.
Re: Strange nested loop behaviour
Post by Richard Russell on Oct 9th, 2015, 6:38pm
on Oct 9th, 2015, 5:05pm, tsh73 wrote:My guess that if last loop instruction executed was "for x=..." and somehow interpreter hit line "next y", it has information that something went wrong. |
|
That isn't necessarily considered 'wrong'. The following program reports no error in QBASIC, BBC BASIC, Liberty BASIC, Just BASIC or LBB:
QBASIC Code: FOR y = 1 TO 4
FOR x = 1 TO 4
PRINT x, y
GOTO skip
NEXT x
skip:
NEXT y
BBC BASIC Code: FOR y = 1 TO 4
FOR x = 1 TO 4
PRINT x, y
GOTO (skip)
NEXT x
(skip)
NEXT y
LB/JB/LBB Code: FOR y = 1 TO 4
FOR x = 1 TO 4
PRINT x, y
GOTO [skip]
NEXT x
[skip]
NEXT y
However, interestingly, the results differ. In QBASIC, BBC BASIC and LBB the following output is generated:
Code:
But in LB 4.04, LB 4.5.0 and Just BASIC the following output is generated:
Code:
Note that in the former case x remains constant and y changes, but in the latter case y remains constant and x changes. LB/JB is doing something very strange, but I do not understand what.
Richard.
Re: Strange nested loop behaviour
Post by wscbill on May 17th, 2017, 3:01pm
Similar results occur in LB using nested DO loops. In my situation the branch out of the inner loop was the result of a menu control handler using a branch address, rather than an explicit "goto" statement. Took a bit of testing to figure out what was going on.
What appears to be happening is that when a FOR or DO loop is encountered, the address of top of the FOR/DO loop is pushed onto a stack. The NEXT/LOOP statements use the address at the top of the stack to loop back.
At the completion of the loop, when NEXT/LOOP statement falls through, the top address is popped off the stack. When loops are nested, the pushes and pops keep the loop addresses synchronized with their respective loop code.
Branching out of the loop and bypassing the NEXT statement, also bypasses the pop. So the loop address is wrong for the code being executed.
In the example code, the branch to [skip] bypasses the "next x", so the loop address of the inner loop (for x=1) is still on the top of the stack. When "next y" is executed, it loops back to "for x=1" , instead of to "for y=1".
Code:
for y = 1 to 3
for x = 1 to 5
if x = 4 then goto [skip]
print x, y
next x
[skip]
next y
Re: Strange nested loop behaviour
Post by Richard Russell on May 17th, 2017, 4:47pm
Both LB and LBB forbid jumping out of a loop using GOTO, and 'anything' might happen if you break this rule. The EXIT statement is specifically provided for this case, so there is no reason ever to risk using GOTO:
Code:
for y = 1 to 3
for x = 1 to 5
if x = 4 then exit for
print x, y
next x
next y
In modern structured programming GOTO is obsolete, and I would suggest it should ideally not be used in any circumstances whatever. However some people feel this is over-zealous.
Richard.