Circular Indexing and FOR loop minimization

As discussed earlier on the post on 'Circular Indexing', considering interpreting indices that lie outside the range 0 to n-1 modulo n for list indexing. Thus element n corresponds to element 0, n+1 to element 1 and so on. This has two consequences: 1.) Makes the Python convention of interpreting index -1 as the last element more logical since -1 is the same as n-1 when taken modulo n. If -1 is the first element then to be logically consistent we have to interpret index n as the element 0 as well! Obviously this does allow certain errors to slip but if you are going to allow an index of -1 (indices that occur before 0) then you might just as well allow indices above n-1 to be logically consistent. 2.) If circular indexing is used then instead of using a double FOR loop to go through a loop twice we can iterate from 0 to 2n ! Although trivial for the case of one dimensional lists/arrays, cylindrical indexing (for 2D arrays) and toroidal indexing (2D, 3D and nD arrays) schemes might be worth exploring for n-dimensional NumPy arrays. Circular and in general cylindrical or toroidal indexing schemes might simplify coding by reducing the number of FOR loops when iterating over nD arrays in NumPy.

Hi Mathew, welcome! My responses interleaved with your comments. On Wed, Nov 25, 2020 at 04:29:17AM +0000, Mathew M. Noel via Python-ideas wrote:
The convention is logical enough as it is. If doesn't need justification based on modular arithmetic.
If -1 is the first element then to be logically consistent we have to interpret index n as the element 0 as well!
No we don't. There is no logical consistency in arguing that index n **must** be interpreted as element 0. Don't mistake your *preference* for circular/modular indexing for *logical necessity*. It is totally logically consistent to treat indexes as *linear* not circular, with zero-based indexing starting from the front of the sequence and negative indexes starting from the end.
Obviously this does allow certain errors to slip
Indeed. That's a much stronger argument against your proposal than the weak argument from "logical consistency" for your proposal.
That doesn't follow even a little bit. We're under no obligation to totally break a perfectly good, useful and logical model for indexing, changing it to a *worse* model, under a dubious and unjustified claim that we "might as well" change the model to be "logically consistent". We don't add language features because we "might as well", we add them because they will improve the language. The current model is already logically consistent. Moving to a circular model might be useful in a tiny fraction of cases, but will be worse for most cases. But the fatal objection is that it will *break backwards compatibility* and therefore change the meaning of existing code. That rules out this change without a long period of deprecating the existing behaviour, followed by adding the new behaviour. And we don't do that just for a small benefit. Two examples of how a circular model will break existing code (there may be more): 1. The "sequence protocol" for iteration. The sequence protocol relies on calling `sequence[0]`, `sequence[1]`, and so forth, for increasing indices 0, 1, 2, 3, 4, ... until IndexError is raised. If we shift to a circular model, IndexError will not be raised, and sequences which are iterable today will turn into infinite loops under your proposal. 2. Slicing past the end index. Under the current design: >>> a = "abcdefgh" >>> a[3:10] 'defgh' Under your proposal: >>> a[3:10] # proposed behaviour 'defghab' Both of these are changes in behaviour, so would need to have a significant benefit over the status quo to justify breaking people's code.
2.) If circular indexing is used then instead of using a double FOR loop to go through a loop twice we can iterate from 0 to 2n !
Do you often need to go through a loop twice? Can you give an example?
I think you have the process backwards. We don't change the language because the proposal "might be worth exploring". Do your exploring first, and only if you find some really useful benefits of the proposal will we consider changing the language. Python is not an experimental toy language with ten users. It is one of the most popular languages in the world, relied upon by tens or hundreds of thousands of people. We don't change existing behaviour and break people's code because it "might be useful".
Same again. *First* prove that it will do these things, then we consider whether the benefit is worth the disruption. If you are claiming a benefit for numpy, have you asked numpy developers what they think of your proposal? -- Steve

Hi Mathew, welcome! My responses interleaved with your comments. On Wed, Nov 25, 2020 at 04:29:17AM +0000, Mathew M. Noel via Python-ideas wrote:
The convention is logical enough as it is. If doesn't need justification based on modular arithmetic.
If -1 is the first element then to be logically consistent we have to interpret index n as the element 0 as well!
No we don't. There is no logical consistency in arguing that index n **must** be interpreted as element 0. Don't mistake your *preference* for circular/modular indexing for *logical necessity*. It is totally logically consistent to treat indexes as *linear* not circular, with zero-based indexing starting from the front of the sequence and negative indexes starting from the end.
Obviously this does allow certain errors to slip
Indeed. That's a much stronger argument against your proposal than the weak argument from "logical consistency" for your proposal.
That doesn't follow even a little bit. We're under no obligation to totally break a perfectly good, useful and logical model for indexing, changing it to a *worse* model, under a dubious and unjustified claim that we "might as well" change the model to be "logically consistent". We don't add language features because we "might as well", we add them because they will improve the language. The current model is already logically consistent. Moving to a circular model might be useful in a tiny fraction of cases, but will be worse for most cases. But the fatal objection is that it will *break backwards compatibility* and therefore change the meaning of existing code. That rules out this change without a long period of deprecating the existing behaviour, followed by adding the new behaviour. And we don't do that just for a small benefit. Two examples of how a circular model will break existing code (there may be more): 1. The "sequence protocol" for iteration. The sequence protocol relies on calling `sequence[0]`, `sequence[1]`, and so forth, for increasing indices 0, 1, 2, 3, 4, ... until IndexError is raised. If we shift to a circular model, IndexError will not be raised, and sequences which are iterable today will turn into infinite loops under your proposal. 2. Slicing past the end index. Under the current design: >>> a = "abcdefgh" >>> a[3:10] 'defgh' Under your proposal: >>> a[3:10] # proposed behaviour 'defghab' Both of these are changes in behaviour, so would need to have a significant benefit over the status quo to justify breaking people's code.
2.) If circular indexing is used then instead of using a double FOR loop to go through a loop twice we can iterate from 0 to 2n !
Do you often need to go through a loop twice? Can you give an example?
I think you have the process backwards. We don't change the language because the proposal "might be worth exploring". Do your exploring first, and only if you find some really useful benefits of the proposal will we consider changing the language. Python is not an experimental toy language with ten users. It is one of the most popular languages in the world, relied upon by tens or hundreds of thousands of people. We don't change existing behaviour and break people's code because it "might be useful".
Same again. *First* prove that it will do these things, then we consider whether the benefit is worth the disruption. If you are claiming a benefit for numpy, have you asked numpy developers what they think of your proposal? -- Steve
participants (3)
-
Mathew M. Noel
-
Serhiy Storchaka
-
Steven D'Aprano