-1

I'm working on a simple left circular shift function as an exercise (ie feed it [1,2,3] and it returns [[1,2,3],[2,3,1],[3,1,2]]). If I use this version of the code:

def left_shifts(L):
    if len(L) == 0:
        M = []
    elif len(L) == 1:
        M = deepcopy(L)
    else:
        M = [len(L)]
        M[0] = L
        for i in range(1,len(L)):
            M[i] = (L[i:] + L[:i])

    return M

print (left_shifts([1,2,3,4,5]))

I get an error that the list assignment index is out of range. However, if I just tweak it to this:

def left_shifts(L):
    if len(L) == 0:
        M = []
    elif len(L) == 1:
        M = deepcopy(L)
    else:
        M = [len(L)]
        M[0] = L
        for i in range(1,len(L)):
            M.append((L[i:] + L[:i])) #changed line

    return M

print (left_shifts([1,2,3,4,5]))

It works fine. My question is: why would the upper code generate this error? All I'm doing is adding two lists together and assigning it to one element of another list, which I would think would be legal.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Chase
  • 61
  • 6
  • @TigerhawkT3: That's not an applicable. I think the OP thought that `M = [len(L)]` created a list of `len(L)` elements, at which point it is not unreasonable to think that `M[1]` exists. – Martijn Pieters Mar 14 '17 at 05:47
  • @MartijnPieters - The answer explains the exact problem. I think it's a precise duplicate. – TigerhawkT3 Mar 14 '17 at 05:50
  • @TigerhawkT3: it doesn't cover `[integer]`, then expecting `integer` number of elements to exist. – Martijn Pieters Mar 14 '17 at 05:54
  • @MartijnPieters - The only possibility for an even closer duplicate would basically be "how do I create a list in Python?", which would be answered by any tutorial. – TigerhawkT3 Mar 14 '17 at 05:56

2 Answers2

1

M = [len(L)] creates a list with one element, an integer:

>>> L = [1, 2, 3]
>>> len(L)
3
>>> [len(L)]
[3]

This is why M[1] doesn't exist; you have a list of length 1 so only index 0 exists.

If you expected that to create a list with len(L) elements, you'd need to use multiplication:

M = [None] * len(L)

This creates a list with len(L) references to None. Don't use list multiplication for mutable objects however; [...] * N doesn't create copies of the contents; you get N references to the same contents. This matters when those contents themselves are mutable.

Not that your code needs to be this complicated. Just use a list comprehension:

def left_shifts(L):
    return [L[i:] + L[:i] for i in range(len(L))]

Demo:

>>> def left_shifts(L):
...     return [L[i:] + L[:i] for i in range(len(L))]
...
>>> left_shifts([1, 2, 3])
[[1, 2, 3], [2, 3, 1], [3, 1, 2]]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • "What should indices 0 through to 41 all be set to in that case?" - Some languages allow sparse arrays, and they generally use a null value (or 0, or garbage data). – TigerhawkT3 Mar 14 '17 at 05:44
  • @TigerhawkT3: There are sparse array implementations for Python too, but Python lists aint such a beast. Sparse arrays are specialised structures, not suited to be a general sequence workhorse like `list` is. :-) – Martijn Pieters Mar 14 '17 at 05:46
  • @TigerhawkT3: I dropped the index assignment vs. appending altogether, as I'm now pretty sure the OP meant to create a list of `len(L)` elements. – Martijn Pieters Mar 14 '17 at 05:49
  • This is true, I'm coming from a C/C++ background and didn't realize this syntax doesn't carry over to Python. – Chase Mar 15 '17 at 20:21
1

I think the best way to explain this is by modifying your code slightly:

def left_shifts(L):
    if len(L) == 0:
        M = []
    elif len(L) == 1:
        M = deepcopy(L)
    else:
        M = [len(L)]
        print(M)
    return M

print (left_shifts([1,2,3,4,5]))

So I changed it to print what you just assigned M to.

It outputs:

[5]

You just made an array with the number 5 in it... Array definitions in Python don't work this way. Python uses a unique syntax. Additionally, [] defines a list, not an array.

Just use the append function.

New code:

def left_shifts(L):
    if len(L) == 0:
        M = []
    elif len(L) == 1:
        M = deepcopy(L)
    else:
        M = []
        M.append(L)
        for i in range(1,len(L)):
            M.append((L[i:] + L[:i])) #changed line

    return M

print (left_shifts([1,2,3,4,5]))
Neil
  • 14,063
  • 3
  • 30
  • 51