2

I have following code (produces an elementary cellular automaton):

def cellular_automaton(init_string,pattern,gens):
    values=[128,64,32,16,8,4,2,1]
    pattern_list=[]
    k=0
    while k<len(values):
        if values[k]+sum(pattern_list)<=pattern:
            pattern_list.append(values[k])
        k=k+1



    i=0
    j=0
    b=[]
    f=''

    pos_init=[0,1,0]
    pos_interm=[]
    pos_init=[[n,n+1,n] for n in range(len(init_string)-1)]
    pos_interm.append(pos_init)
    pos_interm[-1][-1].append(len(init_string)-1)
    pos_interm[-1][-1].append(0)
    pos_interm[0][0].insert(0,len(init_string)-1)
    pos_interm2=[val for subl in pos_interm for val in subl]
    pos=[val for subl in pos_interm2 for val in subl]

    b.append(pos)
    while j<gens:
        pos=[i+len(init_string) for i in pos]
        b.append(pos)
        j=j+1
        c=[val for subl in b for val in subl]



    while i<len(c):
        if init_string[c[i]]=='.' and init_string[c[i+1]]=='.' and init_string[c[i+2]]=='.':
            if 1 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='.' and init_string[c[i+1]]=='.' and init_string[c[i+2]]=='x':
            if 2 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='.' and init_string[c[i+1]]=='x' and init_string[c[i+2]]=='.':
            if 4 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='.' and init_string[c[i+1]]=='x' and init_string[c[i+2]]=='x':
            if 8 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='x' and init_string[c[i+1]]=='.' and init_string[c[i+2]]=='.':
            if 16 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='x' and init_string[c[i+1]]=='.' and init_string[c[i+2]]=='x':
            if 32 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='x' and init_string[c[i+1]]=='x' and init_string[c[i+2]]=='.':
            if 64 in pattern_list:
                init_string=init_string+'x'
            else:
                init_string=init_string+'.'
        elif init_string[c[i]]=='x' and init_string[c[i+1]]=='x' and init_string[c[i+2]]=='x':
            if 128 in pattern_list:
               init_string=init_string+'x'
            else:
                init_string=init_string+'.'





        i=i+3





    return init_string[(len(pos_interm2)+1)*-2:(len(pos_interm2)+1)*-1]

The output is produced is as follows:

print cellular_automaton('.x.x.x.x.', 17, 2)

This code works perfectly well in Python 3.3.2 but produces a 'string index out of range' error in Python 2.7:

Traceback (most recent call last):
  File "vm_main.py", line 33, in <module>
    import main
  File "/tmp/vmuser_trwqmlfqgq/main.py", line 143, in <module>
    print cellular_automaton('.x.x.x.x.', 17, 2)
  File "/tmp/vmuser_trwqmlfqgq/main.py", line 95, in cellular_automaton
    if init_string[c[i]]=='.' and init_string[c[i+1]]=='.' and init_string[c[i+2]]=='.':
IndexError: string index out of range

I'm not so familiar with the differences between Py2 and Py3, so I'd be glad if anyone could help me find out what is to be done to make it work in Python 2.7 as well. How come it produces this Index error in Py2?

smci
  • 32,567
  • 20
  • 113
  • 146
Revilo79
  • 31
  • 2
  • 3
    `print cellular_automaton('.x.x.x.x.', 17, 2)` <- that’s not valid Python 3 syntax. – poke Sep 28 '13 at 15:36
  • 1
    take pdb and figure it out –  Sep 28 '13 at 15:38
  • `print` is a function in 3.x – rlms Sep 28 '13 at 15:38
  • 1
    The print is correctly written in Python 3, I copied and pasted the above code version from Python 2. As said, the code does work and produces the correct output in Python 3.3.2. – Revilo79 Sep 28 '13 at 15:46
  • @Revilo79 Fair enough; you can just keep the parentheses there for Python 2 though, so the same code is working in both Python 2 and 3. – poke Sep 28 '13 at 15:47
  • Your while loop is the ugliest piece of python code I've seen in a while, just do `test_str = ''.join(init_string[x] for x in [c[i], c[i+1], c[i+2]])` and then test against that string instead of repeating yourself a hundred times... same thing for the `+=` assignment, save the value to be appended in a variable and do `init_string += value` _once_ at the end of the loop; or use a dict to map the values. – l4mpi Sep 28 '13 at 16:27
  • @l4mpi: Thanks for your advice. I've started to learn coding a couple of weeks ago and have not encountered 'join' so far. Long way to go.. – Revilo79 Sep 28 '13 at 18:47

1 Answers1

11
pos=[i+len(init_string) for i in pos]

In Python 2, this will leak out the last value of i overwriting your original initialized value of 0. As such, Python 2 will start at index 8 which will later result in the index errors.

To fix this, just give that variable a different name:

pos=[k+len(init_string) for k in pos]

To show a clearer example of this, you can test this in an interpreter session. Python 3 will correctly keep the original value of i:

>>> i = 0
>>> [i for i in range(5)]
[0, 1, 2, 3, 4]
>>> i
0

Python 2 however will keep the last value of the loop within the list comprehension:

>>> i = 0
>>> [i for i in range(5)]
[0, 1, 2, 3, 4]
>>> i
4

For more on this change, see also this answer.

Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602
  • Excellent. Love learning something like this from an unexpected question. – beroe Sep 28 '13 at 16:20
  • @poke So in Python3.X list compression has its own namespace (ture for dict compression too) **?** – Grijesh Chauhan Sep 29 '13 at 14:41
  • 1
    @GrijeshChauhan That behavior was always true for generator expressions (technically required actually); and with Python 3, comprehensions were changed to match this behavor for consistency. So yes, all comprehensions (list, dict and set) work like that now as they are internally using generators. – poke Sep 29 '13 at 15:12