LoginLogin

FOR variable changes

Root / Programming Questions / [.]

SquareFingersCreated:
When you encounter
FOR variable = from-value TO to-value
you expect that every time the loop is executed, the same variable is incremented and checked against the to-value, to see if the loop should stop. This is not the case.
DIM A[100]
A[5]=80
J=1
FOR A[J]=1 TO 15
PRINT J
J=J+1
NEXT
You might expect 15 lines, starting with "1" and ending with "15". Instead, you get four lines, "1" to "4". When the FOR statement is first encountered, J has the value 1, and the variable A[1] is initialized to 1. Inside the loop, J is incremented. When the NEXT is reached, the program goes back to the FOR. Now, A[2] is incremented, and checked against the to-value, 15. Next time, A[3] is incremented and checked. In fact, it is possible that a FOR statement can be looped back to many times, and eventually cause a "Subscript out of range error". The variable of a FOR loop is not always the variable of the FOR loop. EDIT: I regret my choice of wording in this post. By the phrase "You might expect", I did not mean to imply that that was my expectation, or that the expectation of any particular individual is relevant to the bug report. A more proper phrase that I should have used is "A close reading of the SmileBasic documentation might lead one to expect". I apologize for the sloppiness of my expression.

Well, pbbptthbtthh.

BTW are these bugs being reported to SmileBoom?

That doesn't sound like a bug to me; that's exactly how it should work.

This bug is perhaps more a matter of expectation. InstructionList.pdf refers to "Loop variable" being between the "FOR" keyword and the "=" sign. This has the implication that associated with each loop, there is a particular variable. That implication is false.

In this case, the loop variable in question is A[J], not J. If you're changing J inside the loop, when the loop condition is checked, it will still use A[J] (which is at the next index now). From a semantic point of view, it wouldn't make sense to continue using the original value of A[J] present at the start of the loop. If this is not what you want, use A[1]. This is true in many languages:
int i = 0;
int a[100];
a[5] = 80;

for(a[i] = 0; a[i] < 15; a[i]++)
{
   i++;
}
I think it would be a messy design if the variable was "captured" at the start of the loop, as you wouldn't be able to alter the loop variable within. What behavior did you expect?

The C version of "for" is abstracted quite far; the three components inside the parentheses are: - an expression to be evaluated before the loop begins - an expression to check for termination of the loop - an expression to be evaluated at the end of each iteration The first and third expressions typically have side-effects, but you can see, there is no concept of a 'loop variable' in the C 'for'. My point is that if the documentation refers to a 'loop variable', the expectation is that there is a variable that is associated with the loop. That is not true. If it is easier to think of this as a bug in the documentation, so be it. It is also incorrect to call the value after the "TO" keyword an "end value". A value is static. What comes after "TO" is evaluated each loop. It is more properly called an "expression", rather than a "value". (EDIT: The code after "STEP" should also be called an expression, rather than a value, for the same reason.)

It's my expectation that NEXT increments and continues the variable currently scoped in the loop, no matter where it's location in the code. So this is an error in my eyes. Ah wait I understood. Ignore me.

Well, there's no point arguing I guess. I think the current semantics make sense, and that the term "loop variable" is vague enough to cover this case. I'm sorry that the manual does not live up to your expectations; maybe at some point we'll have a fan-made manual that is 100% correct.

The current semantics make sense if they are explained properly, and the vagueness is the problem. And, I'm uncomfortable with making this a personal issue. It is not just my expectations. Any programmer coming from Python, for instance, might expect the code in the top post to be the same as
a=[0]*100
a[5]=80
j=1
for a[j] in range(1,16):
 print j
 j=j+1
But it is not, because in Python, the loop variable is the loop variable. (Also, in Python, the "to-value" that determines when the loop ends is an expression that is evaluated just once.) EDIT: NeatNit has helped me realize that in Python, the loop variable is not the loop variable. On reflection, this is not a surprise, because the Python specification does not refer to such a thing as a 'loop variable', and trying to impose a concept foreign to the language onto the language can easily lead to confusion. Never mind. In fact, I don't want SmileBasic to be like Python, particularly. I want SmileBasic to be like the SmileBasic documentation, and the SmileBasic documentation indicates that there is a 'for loop variable'.

Oh, I wasn't making it a personal issue! I'm sorry if I made it sound like that; I didn't mean that it was only your expectations that aren't being met. There are certainly others who are unhappy with the state of the manual too; I just didn't want to push my opinion anymore.

In most programming languages, a for loop is just a fancy while loop. Its syntax, as mentioned by randomouscrap, is usually as follows:
 for (initializer; test; increment)
{
    looping code;
}
In this code, all 3 expressions are completely independent: The initializer can be any expression at all, but normally just sets a variable:
i = 0
The test can be any boolean expression, but is normally a test of range:
i < 15
The increment can be any expression at all, but normally increments the variable:
i++
It is, for all intents and purposes, the exact same thing as:
initializer;
while(test)
{
    looping code;
    increment;
}
(With the only exception that in this format, the initializer would create variables with a bigger scope than just the while loop) The important thing to note about this definition of a for loop is that it is always concretely defined for any situation. If you change i inside the loop, there are no anomalies because the loop doesn't expect i to be anything specific - it just executes whatever expression increment is and checks the boolean result of whatever expression test is. It doesn't care about anything else. For SmileBASIC, it's drastically different. The FOR loop is given a specific variable, an initial value, and a final value. The initializer is still quite simple, but the test is not standalone, and the increment is not standalone.
FOR I = 0 TO 9
NEXT
For the above loop, the following questions arise: 1. Is I saved as an expression, or a specific variable? 2. If we change the value of I inside the loop, will it remember what state it was and jump to the next one at the next loop, or will it increment the new value by 1? 3. Is 9 saved as an expression, or a specific value that's only evaluated once? The question that you've encountered is 1: is I saved as an expression. Basically, I is a fairly simple, static expression that can always refer to just one variable. However, if you put in a more complicated expression such as A[J], the value of J can be changed and the resulting target variable (or in this case, array element) is a different one than it was before. A[J] is the expression referring to the Loop Variable, and it is NOT a static one so it can change. The important thing to understand here is that when you use a FOR loop, it's usually for one of two reasons: A. You're looping the same code a set number of times, known in advance B. Same as A, but the code also needs to know how many times it's looped already, causing the code to be run in a slightly different way. An easy example for A is:
FOR I = 1 TO 5
PRINT "HELLO WORLD!"
NEXT
Which would print HELLO WORLD! to the screen 5 times. An easy example of B is:
FOR I = 1 TO 5
PRINT "Number "; I
NEXT
Which would print out:
Number 1
Number 2
Number 3
Number 4
Number 5
And a different example of B usage:
DIM A[5]
FOR I = 0 TO 4
A[I] = 40
NEXT
Which would create an array called A and initialize all its elements to the value 40. In all of these examples, notice that the value of I is ONLY changed by the FOR loop, never by the code inside the loop. Changing the value of the loop variable inside the loop is called undefined behavior - you're not supposed to do it, but it doesn't cause a crash. It is unknown whether it will do one thing or another - its actual result might be consistent, it might even be useful, but it was never intentionally designed to do that. This brings us back to your example, which I've simplified below for convenience:
DIM A[5]

I = 0
FOR A[I] = 0 TO 4
PRINT I, A[I]
I = I + 1
NEXT
What are we doing here? Not only are we changing the loop variable - we're actually changing what the loop variable IS. When we're done changing it, it's not the same array element that it was before - it's a different one altogether, it's a different memory address. This is even more undefined than changing the value of the variable mid-loop. Again, it might have a consistent result and it might be useful, but it's UNDEFINED. It was never intended to ever encounter this scenario. This is not a bug. When I read through the code you posted, I expected it to do exactly what you said it ended up doing. TL;DR as mentioned already, the loop variable is A[J], and not J. If you want it to be J, you write it as J, not A[J]. I hope I haven't made too much of a mess.

The FOR loop is given a specific variable, an initial value, and a final value. No - there is no 'specific variable', there is an expression (as you write yourself later: 1: is I saved as an expression). More precisely, an L-value expression, one that may be evaluated numerous times, and is not guaranteed to give the same L-value (variable) each time. There is a particular variable for each iteration of the loop, but in general, there is no particular variable for the loop. The important thing to understand here is that when you use a FOR loop, it's usually ... The specification of a command should cover all uses of the command, not just the 'usual' ones. This is not a bug. When I read through the code you posted, I expected it to do exactly what you said it ended up doing. Except meeting your expectations does not mean it's not a bug. Performing as specified means it's not a bug. InstructionList.pdf is the closest thing I have seen to specifications, it calls the L-value expression between the "FOR" keyword and the "=" symbol a "loop variable". SmileBasic's FOR loops do not have a "loop variable" per se - it is a bug.

The FOR loop is given a specific variable, an initial value, and a final value. No - there is no 'specific variable', there is an expression (as you write yourself later: 1: is I saved as an expression). More precisely, an L-value expression, one that may be evaluated numerous times, and is not guaranteed to give the same L-value (variable) each time. There is a particular variable for each iteration of the loop, but in general, there is no particular variable for the loop.
Well, I was talking about the correct usage of the for loop, in which it's given a single variable to initialize and use and which doesn't suddenly change within the loop. If you want to get technical then yes, we can call it an L-value expression. It is YOUR responsibility as the programmer to provide an expression which always results in the same L-value.
The important thing to understand here is that when you use a FOR loop, it's usually ... The specification of a command should cover all uses of the command, not just the 'usual' ones.
It does cover all correct uses of the command. Any uses which don't conform to the specification (like yours) fall under undefined behavior and should never be used, unless you really like pain.
This is not a bug. When I read through the code you posted, I expected it to do exactly what you said it ended up doing. Except meeting your expectations does not mean it's not a bug. Performing as specified means it's not a bug. InstructionList.pdf is the closest thing I have seen to specifications, it calls the L-value expression between the "FOR" keyword and the "=" symbol a "loop variable". SmileBasic's FOR loops do not have a "loop variable" per se - it is a bug.
Again, they do have a variable when used CORRECTLY. When you use something incorrectly and it produces strange results, it's not a bug. I don't call my TV manufacturer to complain that it doesn't toast my bread when I push it through the hole I carved through the screen. And besides that, don't forget that SmileBASIC is intended for newbies and veterans alike. Newbies would have enough trouble as it is understanding how FOR loops work and how variables work. It would be really stupid to confuse them with terms like expression or L-value when the term 'variable' is consistent with the rest of the doc, the assumed level of knowledge and all correct usages of the function, and gets the idea across just fine. By the way, in that original snippet, what would you expect would happen? Should it just increase A[1] while A[J] refers to something else by then?
DIM A[100]
A[5]=80
J=1
FOR A[J]=1 TO 15
PRINT J
J=J+1
NEXT

Ah, you're changing your argument from 'usual' usage to 'correct' usage. There's something to be said for that argument. However, the television analogy is way off. It's more like the TV instruction manual tells you what will happen if you press the "volume up" button with your thumb, but fails to mention that something different happens if you press it with your index finger (that being 'incorrect usage' of the button). Substituting a thumb with an index finger is something a person might easily do without a second thought unless there is an explicit instruction not to, and substituting a constant L-value expression with a varying one is something a person might easily do too. Since it takes a more advanced user to even recognize that there is a difference, of course it's a substitution a beginner might make. I disagree with the conclusion that since the target audience is partly (or indeed, mostly) newbies, there should be no proper technical documentation. My expectations are beside the point (I have already written, I'm uncomfortable with making this a personal issue. It is not just my expectations.), but I expected that one of two things would happen: either (a) the documentation was correct, that there is such a thing as a "for loop variable" in SmileBasic, and that the loop would behave something like Python, or, (b) that the documentation was misleading and that this dialect of Basic was like others I had encountered, and that the loop would behave like C. As it turns out, I was correct, one of those two things did happen.

My expectations are beside the point, but I expected that one of two things would happen: either (a) the documentation was correct, that there is such a thing as a "for loop variable" in SmileBasic, and that the loop would behave something like Python, or, (b) that the documentation was misleading and that this dialect of Basic was like others I had encountered, and that the loop would behave like C. As it turns out, I was correct, one of those two things did happen.
Python behaves the exact same way. My code:
a=[0]*100
a[5]=80
j=1
for a[j] in range(5,16):
    print "j =",j
    print "a[j] =",a[j]
    print "a[1] =",a[1]
    print
    j=j+1
My testing grounds: http://www.skulpt.org/ My output:
j = 1
a[j] = 5
a[1] = 5

j = 2
a[j] = 6
a[1] = 5

j = 3
a[j] = 7
a[1] = 5

j = 4
a[j] = 8
a[1] = 5

j = 5
a[j] = 9
a[1] = 5

j = 6
a[j] = 10
a[1] = 5

j = 7
a[j] = 11
a[1] = 5

j = 8
a[j] = 12
a[1] = 5

j = 9
a[j] = 13
a[1] = 5

j = 10
a[j] = 14
a[1] = 5

j = 11
a[j] = 15
a[1] = 5
Python behaves EXACTLY like SmileBASIC does. I rest my case.

That's not exactly how SmileBASIC seems to be handling things, either, Neat. What you have there is changing A[5] from 80 to 9. The code in OP increments the 80 by one and stops because that's over 15. I'm pretty sure this is all documented well enough, though. Do we want them to cover how variables and arrays work every time they show up? That would be a really long manual. I don't think they need to tell us that the FOR variable is global if all variables are global by default. It's clearly indicated if and when variables are going to be treated as local. See DEF if you need an example because I think that's about the only one. When it says "loop variable" it's talking about a variable that's global so the first example makes perfect sense granted you understand that you're setting the value first and then checking it. That's what the = is for. Assignment and assignment alone. So, yes. The documentation is correct. There is such a thing as a "loop variable" and it works just like every other variable with the addition of behind-the-scenes counting duties. As a matter of fact, it can be an existing variable if you want. SquareFingers did that. It's just that using the variable elsewhere is a bad idea. It gets used as a counter, after all, so it gets changed every time the loop runs. The value after TO is also most definitely a value. It never changes. (It might get used in an expression behind the scenes but that doesn't change what it is in-line.) You got four lines printed because A[5] was already set to 80. The way FOR works here, it sets and checks the value the first time but adds to and checks it after that. If you add "PRINT A[5]" at the end, you should see that it has incremented as well. Try this:
DIM A[100]
A[3]=11
A[5]=80
J=1
 FOR A[J]=0 TO 79
  PRINT "A[";J;"]=",A[J]
  J=J+1
 NEXT
PRINT "A[";J;"]=",A[J]
And you should get:
A[1]=   0
A[2]=   1
A[3]=   12
A[4]=   1
A[5]=   81
It's the same as the example in the OP but you can clearly see what's going on now. Each value gets incremented, as the documentation explains it should, and the loop stops when the current value of the loop variable reads as over the end value. To get the results in NeatNit's Python example, you'd probably need to use a version of WHILE instead of FOR. Still, it's not a bug. It works exactly as intended and exactly as explained in the documentation. It might be a good idea to check out the bottom half of http://smilebasic.com/en/spec/ if you're having trouble with the notation. There's some good information on there that's a little difficult to locate in the e-manual. And remember, this is SmileBASIC. It's not necessarily like anything else you've worked with, including other versions of BASIC.

DIM A[100]
A[5]=80
J=1
FOR A[J]=5 TO 15
 PRINT J,A[J],A[1]
 J=J+1
NEXT
generates
1 5 5
2 1 5
3 1 5
4 1 5
Python behaves EXACTLY like SmileBASIC does.
No.

That would be a really long manual.
Only if it were poorly written. Oh wait... for a moment, I forgot where I was.
I don't think they need to tell us that the FOR variable is global ...
The issues of global variables vs. local variables are very different, and really should go in a thread other than this one.
There is such a thing as a "loop variable"
I'm afraid not, and this is one point that NeatNit and I agree on. As he writes: "the resulting target variable (or in this case, array element) is a different one than it was before", i.e. there is no single variable for the loop as a whole.
It's just that using the variable elsewhere is a bad idea. It gets used as a counter, after all, so it gets changed every time the loop runs.
In my code, I did not change any of the 'counter' variables. The counter variables are A[1], A[2], etc, and I don't touch any of those.
The value after TO is also most definitely a value. It never changes.
Wrong again, I'm afraid.
LO=4
HI=10
FOR COUNTER=LO TO HI
PRINT COUNTER
HI=HI+0.5
NEXT
This code prints all the numbers from 4 to 16 inclusive.
Still, it's not a bug. It works exactly as intended and exactly as explained in the documentation.
Given the discussion so far, I'm afraid I'll need you to clarify your stance. Question: Does it appear to you that the documentation indicates that, for each FOR loop, there is one particular 'counting variable'? Question: Does it appear to you that, in reality, for each FOR loop, there is one particular variable that gets incremented every pass through that loop? It is critical to keep in mind, when answering these questions, that "the FOR loop" refers to a different thing that "one iteration of the FOR loop".
It might be a good idea to check out the bottom half of http://smilebasic.com/en/spec/ if you're having trouble with the notation.
It is a helpful page. But, I don't see anything on it of particular relevance to this problem. Can you be more specific?
It's not necessarily like anything else you've worked with, including other versions of BASIC.
What I want SmileBasic to be like is SmileBasic documentation.

DIM A[100]
A[5]=80
J=1
FOR A[J]=5 TO 15
 PRINT J,A[J],A[1]
 J=J+1
NEXT
generates
1 5 5
2 1 5
3 1 5
4 1 5
Python behaves EXACTLY like SmileBASIC does.
No.
Because Python's range() generates a list, it doesn't increment anything at any point. The point is that in python, the "loop variable" works the exact same way - if you change its address mid-loop, the FOR assignment of the next value will happen to the new address.