4

I was trying to make a brainf**k interpreter in Python, and came up with an idea of how I can do loops, because I'm having problems with that, but now I need to make the index go back in a for loop this way:

    for i in range(20):
        if i == 15:
            # Set i to 5
        print(i)

But I don't need to replace 15 with 5, but to go back in the index: Real result:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    5
    16
    17
    18
    19

Expected result:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    5
    6
    7
    8
    9

How I can do this, so I can go back in a or loop?

TheCrimulo
  • 433
  • 1
  • 4
  • 14

5 Answers5

5

Its not possible with a simple for…loop.

It can be performed with a while loop and a counter:

i = 0
while i < 20:
    i += 1

    if i == 15:
        i = 5
    print(i)

Another solution is to use a coroutine:

def co_counter():
    i = 0
    new_i = None
    while i < 20:
        if new_i is not None:
            i = new_i - 1
        new_i = yield i
        i += 1

counter = co_counter()
for i in counter:
    if i == 15:
        counter.send(5)
    print(i)
aluriak
  • 5,559
  • 2
  • 26
  • 39
  • @haifzhan OP would have had an infinite loop anwyays had the original code worked – OneCricketeer Mar 29 '16 at 16:08
  • The first example helped me solve out the problem, i just replaced the for loop with the while loop. I know i have an infinite loop, but in the brainf**k code, it will not always be infinite. I dont like much iterating over various sequences, this is just what i needed. – TheCrimulo Mar 29 '16 at 16:20
1

Unfortunately, the indexer in for loop Python will be assigned every time it iterates to the new item. Thus, your one time reassignment:

if i == 15:
    i = 5

won't help much in the subsequent iteration

From the Python for statement documentation:

The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the expression_list. The suite is then executed once for each item provided by the iterator, in the order of ascending indices. Each item in turn is assigned to the target list using the standard rules for assignments, and then the suite is executed. When the items are exhausted (which is immediately when the sequence is empty), the suite in the else clause, if present, is executed, and the loop terminates.

In other words, for your item i, no matter what happen in the loop would be reassigned with the next value provided in the iterable object, in your case being set generated by range(20)

#at loop 15
[0 1 2 ... 15 16 19] #i is progressively increasing independent from what happen in the loop block
           ^ #here is i

change i to 5? you can, but only lasting for one loop!
#you print i, it shows 5, and then...

#at loop 16
[0 1 2 ... 15 16 19]
              ^ #still got reassigned at the beginning of the next loop

One way is to create another indexer - independent from the for loop which is reset when your for loop indexer i reaches 15:

j = 0
for i in range(20):
    if i == 15:
        j = 5;
    print(j)
    j = j + 1

Another way, not so recommended, is to reassign i every time after i >= 15 (you choose to fight with the basic indexer re-assignment everytime)

for i in range(20):
    if i >= 15:
        i = i - 10;
    print(i)
Ian
  • 30,182
  • 19
  • 69
  • 107
1

It is not possible by language construct.

A for loop in python is constructed as for loop_param in iterator: block_of_statements

That means the iterator exists and is defined outside of the loop, and the parameter asks the iterator to give it next value. So whatever value you give the parameter, it will continue the original sequence on next iteration.

BUT... you can imagine (and build) special iterators accepting reset conditions.

class resetable_range:
    def __init__(self, val):
        self.max = val
        self.val = 0
    def __iter__(self):
        return self
    def __next__(self):
        val = self.val
        if self.val == self.max:
            raise StopIteration
        self.val += 1
        return val
    def reset(self, val):
        self.val = val

With that object, you can do:

l = resetable_range(5)
for i in l:
    print(i)
    if (i==3):
        l.reset(1)

And you will get:

0
1
2
3
1
2
3
1
2
3
...
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

More systematically, you can iterate over a sequence of ranges and sequences:

for r in [range(1,15),range(5,10)]:
    for i in r:
        print(i)

The first parameter of range is the first value, the second parameter is the successor of the last value.

How do you get these bizarre loops from a Brainf**k interpreter?

Lorenzo Gatti
  • 1,260
  • 1
  • 10
  • 15
-1

Each iteration of the for-loop pulls i from the list created by range. Setting i=5 only changes i for that one iteration. Instead you could try appending two ranges:

for i in range(15) + range(5,10):
    print(i)
bcorso
  • 45,608
  • 10
  • 63
  • 75