Back and forth loop Python

I want to create an infinite loop that counts up and down from 0 to 100 to 0 (and so on) and only stops when some convergence criterion inside the loop is met, so basically something like this:

for i in range(0, infinity):
for j in range(0, 100, 1):
print(j) # (in my case 100 lines of code)
for j in range(100, 0, -1):
print(j) # (same 100 lines of code as above)


Is there any way to merge the two for loops over j into one so that I don't have write out the same code inside the loops twice?

Use the chain method of itertools

import itertools
for i in range(0, infinity):
for j in itertools.chain(range(0, 100, 1), range(100, 0, -1)):
print(j) # (in my case 100 lines of code)


As suggested by @Chepner, you can use itertools.cycle() for the infinite loop:

from itertools import cycle, chain

for i in cycle(chain(range(0, 100, 1), range(100, 0, -1))):
....


As well as the other answers you can use a bit of maths:

while(True):
for i in range(200):
if i > 100:
i = 200 - i


Here's yet another possibility:

while notConverged:
for i in xrange(-100, 101):
print 100 - abs(i)


If you've got a repeated set of code, use a function to save space and effort:

def function(x, y, x, num_from_for_loop):
# 100 lines of code

while not condition:
for i in range(1, 101):
if condition:
break
function(x, y, z, i)
for i in range(100, 0, -1):
if condition:
break
function(x, y, z, i)


You could even use a while True

If you're using Python 3.5+, you can using generic unpacking:

for j in (*range(0, 100, 1), *range(100, 0, -1)):


or prior to Python 3.5, you can use itertools.chain:

from itertools import chain

...

for j in chain(range(0, 100, 1), range(100, 0, -1)):


up = True # since we want to go from 0 to 100 first

while True: #for infinite loop

# For up == True we will print 0-->100 (0,100,1)
# For up == False we will print 100-->0 (100,0,-1)


start,stop,step = (0,100,1) if up else (100,0,-1)
for i in range(start,stop,step):
print(i)

up = not up # if we have just printed from 0-->100 (ie up==True), we want to print 100-->0 next so make up False ie up = not up( True)

# up will help toggle, between 0-->100 and 100-->0


def up_down(lowest_value, highest_value):
current = lowest_value
delta = 1
while True: # Begin infinite loop
yield current
current += delta
if current <= lowest_value or current >= highest_value:
delta *= -1 # Turn around when either limit is hit


This defines a generator, which will continue to yield values for as long as you need. For example:

>>> u = up_down(0, 10)
>>> count = 0
>>> for j in u:
print(j) # for demonstration purposes
count += 1 # your other 100 lines of code here
if count >= 25: # your ending condition here
break


0
1
2
3
4
5
6
7
8
9
10
9
8
7
6
5
4
3
2
1
0
1
2
3
4


This is more of a partial answer than a direct answer to your question, but you can also use the notion of trigonometric functions and their oscillation to imitate a 'back and forth' loop.

If we have a cos function with an amplitude of 100, shifted left and upwards so that f(x) = 0 and 0 <= f(x) <= 100, we then have the formula f(x) = 50(cos(x-pi)+1) (plot of graph may be found here. The range is what you require, and oscillation occurs so there's no need to negate any values.

>>> from math import cos, pi
>>> f = lambda x: 50*(cos(x-pi)+1)
>>> f(0)
0.0
>>> f(pi/2)
50.0
>>> f(pi)
100.0
>>> f(3*pi/2)
50.0
>>> f(2*pi)
0.0


The issue of course comes in that the function doesn't give integer values so easily, thus it's not that helpful - but this may be useful for future readers where trigonometric functions might be helpful for their case.

I had a similar problem a while ago where I also wanted to create values in the form of an infinite triangle wave, but wanted to step over some values. I ended up using a generator (and the range function as other also have been using):

def tri_wave(min, max, step=1):
while True:
yield from range(min, max, step)
yield from range(max, min, -1 * step)


With carefully selected values on min, max and step (i.e. evenly divisible),

for value in tri_wave(0, 8, 2):
print(value, end=", ")


I get the min and max value only once, which was my goal:

...0, 2, 4, 6, 8, 6, 4, 2, 0, 2, 4, 6, 8, 6, 4...


I was using Python 3.6 at the time.

I became curious if it's possible to implement such kind of triangle oscillator without conditions and enumerations. Well, one option is the following:

def oscillator(magnitude):
i = 0
x = y = -1
double_magnitude = magnitude + magnitude

while True:
yield i
x = (x + 1) * (1 - (x // (double_magnitude - 1))) # instead of (x + 1) % double_magnitude
y = (y + 1) * (1 - (y // (magnitude - 1))) # instead of (y + 1) % magnitude
difference = x - y # difference ∈ {0, magnitude}
derivative = (-1 * (difference > 0) + 1 * (difference == 0))
i += derivative


The idea behind this is to take 2 sawtooth waves with different periods and subtract one from another. The result will be a square wave with values in {0, magnitude}. Then we just substitute {0, magnitude} with {-1, +1} respectively to get derivative values for our target signal.

Let's look at example with magnitude = 5:

o = oscillator(5)
[next(o) for _ in range(21)]


This outputs [0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0].

If abs() is allowed, it can be used for simplicity. For example, the following code gives the same output as above:

[abs(5 - ((x + 5) % 10)) for x in range(21)]

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

flutter websocket connection issue