-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix #4889: for...range loop condition #4891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Ok, that code looks much better. Nicely done. |
|
Let’s add a test that matches #4889. Instead of logging the values, just push them into an array and then compare the array with what you’d expect it to be. |
|
Although the intention of this PR is (was) to revert the output of I think this is the minimal case which shows the difference before and after #4853. r = (x for x in [2..1] by 1)As said earlier, the result of r = (x for x in [2..1] by -1000) # r = [2]
r = (x for x in [2..2] by 1000) # r = [2]The reason # CS 2.1.1 and below
for x in [2..1] by 100
# for (x = i = 2; i <= 1; x = i += 100)
# []
for x in [2..1] by -100
# for (x = i = 2; i >= 1; x = i += -100)
# [2]
for x in [2..2] by 100
# for (x = i = 2; i <= 2; x = i += 100)
# [2]
for x in [2..2] by -100
# for (x = i = 2; i >= 2; x = i += -100)
# [2]The condition part |
|
Going back to the example in #4889, modified slightly so that it could become a test: results = []
n = 1
for i in [0..n]
results.push 'i = ' + i
for j in [(i+1)..n]
results.push 'j = ' + jThis produces Now take the same as above, but add results = []
n = 1
for i in [0..n]
results.push 'i = ' + i
for j in [(i+1)..n] by 1
results.push 'j = ' + jThen we get:
The issue raised in #4889 was that It’s a separate question, which I think @zdenko is alluding at in his last comment, as to whether the output from the first example is correct. But I think we should treat that as a separate bug. Could we get one PR that makes the existence or nonexistence of |
|
@GeoffreyBooth my comment about the correctness of the output was regarding the second example, e.g. Besides fixing infinite loop, #4853 also caused a breaking change of the Options: I vote for b). Why the different result? The results from
No (significant) changes before and after #4853: for (x = a; a <= b ? x <= b : x >= b; a <= b ? ++x; --x)The value of increment/decrement step is defined by the relation of
Before #4853: for (x = a; s > 0 ? x <= b : x >= b; x += s)The loop condition basically says if the step is larger then After #4853: for (x = a; a <= b ? a <= x <= b : a >= x >= b; x += s)The loop condition is changed: The original intention of this change was to prevent possible infinite loops. Back then I wasn't paying additional attention to the output (although I should), as all existing tests have passed. Here is the table that shows the difference between the
|
|
I don’t have time right this minute to dig into this, but do you mind opening a PR that’s limited to making As for changing/correcting the interpretation of |
|
Agree. I'll prepare a couple of tests for this one and create another PR for changing the interpretation of |
|
@zdenko If you don’t mind, let’s wrap up this work (both fixing the |
|
@GeoffreyBooth I added a simple test, a code from the example. |
test/control_flow.coffee
Outdated
| result = [] | ||
| for i in [0..n] | ||
| result.push i | ||
| for j in [(i+1)..n] by 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s either have both for loops say by 1 or have neither do so.
If it makes a difference, in the other PR maybe we can copy this test with the other case (like one test with no by, and the other test with two by 1s).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without by 1 the result will be [ 0, 1, 1, 2, 1 ]. I used the same code as the user in #4889 to show the final (expected) result. Adding by 1 to the first loop will make no difference, but it's not actually the point of this PR (or #4889).
The second loop range is [1..1] in the first iteration and [2..1] in the second, and this is the cause of different results if by 1 is used or not.
The purpose of this PR was to revert the compilation of for - range - by so the result would be the same as before #4853.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think we need a PR that produces the result the user expects, if we think that that result is wrong. I thought the plan was to have two PRs:
- one so that
by 1and a lack ofbyare always equivalent - one that fixes the range output to be what you think it should be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, my bad.
In that case, if you agree, I suggest just one PR which will cover both issues, as changed range output is prerequisite for correct by 1 and w/o by output.
|
Thinking out loud, starting with my example above: results = []
n = 1
for i in [0..n]
results.push 'i = ' + i
for j in [(i+1)..n]
results.push 'j = ' + jThis could be rewritten as: results = []
for i in [0..1]
results.push 'i = ' + i
for j in [(i+1)..1]
results.push 'j = ' + jNow is where things get tricky. So the first range is So there are two second The docs actually have an answer to this question. The example for ranges is this: countdown = (num for num in [10..1])Which returns So to unpack the loops: results = []
i = 0
results.push 'i = ' + i
for j in [1..1]
results.push 'j = ' + j
i = 1
results.push 'i = ' + i
for j in [2..1]
results.push 'j = ' + jAnd finally: results = []
i = 0
results.push 'i = ' + i
j = 1
results.push 'j = ' + j
i = 1
results.push 'i = ' + i
j = 2
results.push 'j = ' + j
j = 1
results.push 'j = ' + jAnd so we get So I guess what I was assuming in earlier posts was wrong: a lack of But what are we supposed to do with a range of Since both interpretations of |
@GeoffreyBooth this PR does exactly that. All tests from #4853 are included, I just corrected the empty array result in cases like
I think the opposite. Here is simplified version of how for (x = a; s > 0 ? x <= b : x >= b; x += s) The part for x in [2..1] by 1
# for (x = 2; 1 > 0 ? x <= 1 : x >= 1; x += 1) The result is an empty array because for x in [2..2] by 1
# for (x = 2; 1 > 0 ? x <= 2 : x >= 2; x += 1) The result in this case will be for x in [2..1] by -100
# for (x = 2; -100 > 0 ? x <= 1 : x >= 1; x += -100) This reads as go down from It's probably just me, but I read for x in [2..1] by 1 # = [2]
for x in [2..1] by -100 # = [2]
for x in [2..2] by 1 # = [2]
for x in [2..1] by 0 # = [2]I know this is a breaking change, but I'm totally OK if the current output stays. |
|
I don't mean that we should keep the output of 1.x, of comparing the step against zero. I just mean that |
|
Yes, I get that. I wanted to explain why the result shouldn't be an empty array. |
|
@zdenko I’ve added a few more tests. Do they look correct to you? Assuming they’re good, I’m okay with merging this in. Are there still additional changes you want to make to how ranges/ |
|
Tests are correct. I'll wait with the PR for |
|
Thanks. Maybe open a new issue explaining it, with a little less focus on what the JavaScript output would be and a little more emphasis on what the result (empty array, array with certain values, etc.) would be. If you can explain why the current output is wrong, and others agree, it’s something we can change in 2.x; if the current output is reasonable but not what a more thoughtful approach would be, it would be good to explain what the better result would be and why, and why we should change to it. |
Fixes #4889.
It seems that #4853, besides preventing an infinite loop, also caused a breaking change in the existing code.
The condition which checks the upper and lower
rangebounds, in order to prevent the infinite loop, always allows at least one iteration of theforloop.Example:
The value of
r, prior to #4853 was[], but now it's[2].To me
[]is a bug as I would expect the result to be[2].Given that
r = (x for x in [1..2] by 1000)is[1],r = (x for x in [1..2] by -1000)should give the same result.Anyway, to correct the breaking change caused by #4853, I prepared this PR which reverts the output.
And, I guess whether the result of
for x in [1..2] by -1should be[]or[1]belongs to a separate discussion and PR.