80

Why am I getting this error, from line 5 of my code, when attempting to solve Project Euler Problem 11?

for x in matrix:
    p = 0
    for y in x:
        if p < 17:
            currentProduct = int(y) * int(x[p + 1]) * int(x[p + 2]) * int(x[p + 3])
            if currentProduct > highestProduct:
                print(currentProduct)
                highestProduct = currentProduct
        else:
                break
            p += 1
'generator' object is not subscriptable
Jeremy
  • 1
  • 85
  • 340
  • 366
Matthew Hannah
  • 803
  • 1
  • 7
  • 6

2 Answers2

112

Your x value is is a generator object, which is an Iterator: it generates values in order, as they are requested by a for loop or by calling next(x).

You are trying to access it as though it were a list or other Sequence type, which let you access arbitrary elements by index as x[p + 1].

If you want to look up values from your generator's output by index, you may want to convert it to a list:

x = list(x)

This solves your problem, and is suitable in most cases. However, this requires generating and saving all of the values at once, so it can fail if you're dealing with an extremely long or infinite list of values, or the values are extremely large.

If you just needed a single value from the generator, you could instead use itertools.islice(x, p) to discard the first p values, then next(...) to take the one you need. This eliminate the need to hold multiple items in memory or compute values beyond the one you're looking for.

import itertools

result = next(itertools.islice(x, p))
Jeremy
  • 1
  • 85
  • 340
  • 366
  • 6
    Python's "lists" *are* closer to arrays than to what most other languages would call a list. They're automatically-resizing arrays (which many language call "vectors" or even simply "arrays"). They're not linked lists, and they support constant-type lookup of any element, not just the head, unlike "lists" in most languages. – Jeremy Jan 09 '14 at 22:41
  • 7
    It should be mentioned that converting a generator to a list contradicts with the initial use case of a generator (e.g. saving memory), which might make such a solution useless. – Markus Jun 21 '19 at 12:11
  • 1
    islice call should be `itertools.islice(x, p, None)` Not adding None as stop index makes "p" the stop index. – Dunatotatos Feb 25 '22 at 09:25
2

As an extension to Jeremy's answer some thoughts about the design of your code:

Looking at your algorithm, it appears that you do not actually need truly random access to the values produced by the generator: At any point in time you only need to keep four consecutive values (three, with an extra bit of optimization). This is a bit obscured in your code because you mix indexing and iteration: If indexing would work (which it doesn't), your y could be written as x[p + 0].

For such algorithms, you can apply kind of a "sliding window" technique, demonstrated below in a stripped-down version of your code:

import itertools, functools, operator
vs = [int(v) for v in itertools.islice(x, 3)]
for v in x:
    vs.append(int(v))
    currentProduct = functools.reduce(operator.mul, vs, 1)
    print(currentProduct)
    vs = vs[1:]
Dirk Herrmann
  • 5,550
  • 1
  • 21
  • 47