Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a for loop either increasing or decreasing?

I have to loop between two values where sometimes the first value is less than the second, and some other times the first is greater than the second (I'm working on two cells inside a grid and the first cell can be on the left of the second or vice versa).

With Python, I can specify if a for loop has to decrease or to increase its values, but the result is something like that:

step = 1
if y < x:
    step = -1
for n in range(x, y, step):
    pass

Is there something more "pythonic" to obtain this?

like image 274
Andrea Iacono Avatar asked Sep 29 '15 11:09

Andrea Iacono


People also ask

How do you make a for loop decrease?

How do you make a for loop decrease in Python? Use range() to iterate through a decreasing range with a for-loop. Call range(start, stop, step) with step as -1 to create a range from start down to but not including step . Use a for-loop to iterate through each integer in this range.

Can you increase i in a for loop?

A for loop doesn't increment anything. Your code used in the for statement does. It's entirely up to you how/if/where/when you want to modify i or any other variable for that matter. That's not a for loop, it's an infinite loop.

How do you loop a descending order in Python?

But Python does have a built-in reversed function. If you wrap range() inside reversed() , then you can print the integers in reverse order. range() makes it possible to iterate over a decrementing sequence of numbers, whereas reversed() is generally used to loop over a sequence in reverse order.


3 Answers

Note that using step=-1 is not the same as a range from the smaller to the larger value!

>>> range(3, 7, 1)
[3, 4, 5, 6]
>>> range(7, 3, -1)
[7, 6, 5, 4]

The first one is from 3 to 6, the latter one from 4 to 7.

If that's still what you want, another way would be to use or:

>>> x, y = 7, 3
>>> range(x, y, x < y or -1)
[7, 6, 5, 4]

If you want to include both the lower and upper index, you have to offset the to index:

>>> step = +1 if x < y else -1 # or use that 'or' expression
>>> range(x, y + step, step)
[7, 6, 5, 4, 3]

Otherwise, you could sort the values first, either using min and max or sorted:

>>> x, y = sorted((x, y))
>>> range(x, y)
[3, 4, 5, 6]

Or in one line: range(*sorted((x, y))) (although I don't think this is very readable)


I did some timing analysis, ordering 1000 random x, y pairs (same pairs for each method):

  • x, y = sorted((x, y)) -> ~305µs for 1000 pairs
  • x, y = min(x, y), max(x, y) -> ~235µs for 1000 pairs
  • x, y = (x, y) if x < y else (y, x) -> ~75µs for 1000 pairs

So the ternary operator is the fastest, but in most cases it probably should not matter much, compared to the rest of the code (creating the range etc.)

like image 153
tobias_k Avatar answered Oct 08 '22 22:10

tobias_k


I guess you could do something like this:

for n in xrange(min(x,y), max(x,y)):

Your way is already pretty much pythonic ;)

Edit: Shorter way suggest by @wap26:

for n in xrange(*sorted((x,y))):
like image 21
Cyrbil Avatar answered Oct 08 '22 23:10

Cyrbil


Doesn't do much different to your code, but you could use this to calculate the step

range(x,y,(y-x)/abs(x-y))

For example:

In [10]: x,y = 5,10

In [11]: range(x,y,(y-x)/abs(x-y))
Out[11]: [5, 6, 7, 8, 9]

In [12]: x,y = 10,5

In [13]: range(x,y,(y-x)/abs(x-y))
Out[13]: [10, 9, 8, 7, 6]
like image 39
tmdavison Avatar answered Oct 08 '22 23:10

tmdavison