# Lazily combine ranges dependent on modulo with one loop

I have a positive integer n. I want to lazily loop over a range of integers less than n consisting of a) integers divisible by 3 (excluding zero) in reversed sorted order and b) zero and integers not divisible by 3 in any order. Is this a very stupid solution? I couldn't think of another way of doing it without having to make use two loops.

d = divisor = 3
list(itertools.chain(
itertools.chain(*zip(*(((i),(i-1),(i-2)) for i in range(d*((n-1)//d),0,-d)))),
(0,),range(n-1,d*((n-1)//d),-1)))

>>> n = 18
[15, 12, 9, 6, 3, 14, 11, 8, 5, 2, 13, 10, 7, 4, 1, 0, 17, 16]

>>> n = 17
[15, 12, 9, 6, 3, 14, 11, 8, 5, 2, 13, 10, 7, 4, 1, 0, 16]

>>> n = 16
[15, 12, 9, 6, 3, 14, 11, 8, 5, 2, 13, 10, 7, 4, 1, 0]

>>> n = 4
[3, 2, 1, 0]

>>> n = 3
[0, 2, 1]

>>> n = 2
[0, 1]

>>> n = 1
[0]

• Why do you want zero in the second part? It is divisible by 3 (0 % 3 == 0) May 23, 2015 at 11:41

Code is read far more often than written. So I prefer to make code as readable as possible. Clever code is very often not good code.

I find the posted code very hard to read, and I don't see why it has to be that way.

I believe this simple, straightforward implementation meets your posted requirements and very easy to read.

def gen(n):
for x in range(n - 1, divisor - 1, -1):
if x % divisor == 0:
yield x
yield 0
for x in range(n):
if x % divisor:
yield x

• I agree @janos. I made something very simple very complicated. I was too eager trying to avoid looping over the n range twice. I will leave my post as an example of how not to do things. May 23, 2015 at 15:25
• Nice but you have some bugs: 0 may be anywere in the middle of the second part of the list, and the second part should be random May 23, 2015 at 17:33
• @Caridorc It works fine on my machine. May 23, 2015 at 17:38
• @tommy.carstensen this programme misteriously works well only somewhere... anyhow thanks for testing it for yourself too. My output is pastebin.com/m5mcp8jx May 23, 2015 at 17:52
• @Caridorc The description says "any order", I assume that includes sorted too. I don't see a requirement for shuffled order, and the OP doesn't seem to miss it either ;-) May 23, 2015 at 19:04

You have a beatiful mind, my solution is as follows:

import random

def shuffled(lst):
return sorted(lst, key=lambda *args: random.random())

def lazy_three_reverse(n):
yield from range(n-3, 1, -3)
yield from shuffled([i for i in range(0, n) if i % 3 != 0]+[0])


Testing:

>>> list(lazy_three_reverse(18))
[15, 12, 9, 6, 3, 5, 14, 10, 11, 7, 8, 4, 16, 17, 13, 2, 0, 1]

• Python 3.x only. May 23, 2015 at 11:47
• Definitely more readable than my solution :) But you loop over the full range of integers twice. It makes almost no difference to the runtime of my code. It's just one of those optimisations I wanted to do for the fun of it :) I should probably prioritise readability instead. May 23, 2015 at 12:48
• Your solution fails for n=17 and n=16. May 23, 2015 at 12:56
• For people like myself that didn't know about yield from, then it's a new feature since version 3.3 described here: docs.python.org/3/whatsnew/3.3.html#pep-380 May 23, 2015 at 14:25
• @tommy.carstensen on my computer it works... weird May 23, 2015 at 17:32