2

I want to know whether we can specify the next value to increment in "for loop" by hard coding?

Currently, I am iterating this way: Eg:

for i in range(0, 10, 2):
    print(i)

output will be 0,2,4,6,8

If I want a value of 5 and 7 along with increments of 2, how can I do that?

Eg:

for i in range(0, 10, 2, 4, 5, 6, 7, 8):
Massifox
  • 4,369
  • 11
  • 31
user1234
  • 257
  • 2
  • 13

3 Answers3

4

If I understand your question correctly, this code is for you: Using generators comprehension:

pred = i%2 is 0
forced_values = [5, 7]

list((i for i in range(0, 10) if pred or i in forced_values))
# output: [0, 2, 4, 5, 6, 7, 8]

or equivalently:

sorted(list(range(0, 10, 2)) + forced_values)

Comparison of execution times:

Benchmark:

n = 10000000 # size of range values
m = 10000000 # size of forced_value list

1. Solution with generators comprehension:

%%timeit
list((i for i in range(0, n) if i%2 is 0 or i in range(0, m)))
# 3.47 s ± 265 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

2. Solution with sorting:

%%timeit
sorted(list(range(0, n, 2)) + list(range(0, m)))
# 1.59 s ± 11.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

or with unordered list, if the order is not important:

%%timeit
list(range(0, n, 2)) + list(range(0, m))
# 1.03 s ± 109 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

3. Solution proposed by @blhsing with more_itertools package, and specifically collate function:

%%timeit
l = []
for i in collate(range(0, n, 2), range(0, m)):
    l.append(i)
# 6.89 s ± 886 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

The best solution, even on very large lists, seems to be the second one that is 2 to 4 times faster than the other proposed solutions.

Massifox
  • 4,369
  • 11
  • 31
  • Skip the list part though and keep it a generator. `for i in (i for i in range(0,1000) if i%20 is 0 or i in [178, 235, 650]): print(i)` – micke Sep 17 '19 at 21:25
  • Perfect! I'm glad I helped you. I changed the code a bit to make it clearer :-) – Massifox Sep 17 '19 at 21:26
  • @micke Obviously it can keep the generator or turn it into a list. The advice is to turn the generator into a list only when it is actually necessary to work on the individual values. All in your previous comment when you do `for i in (i for i in range (0,1000) if i% 20 is 0 or i in [178, 235, 650]): print (i)` you're implicitly turning the generator into a list :-) – Massifox Sep 17 '19 at 21:35
  • Personally, I'm disappointed that you gave in to @blhsing. You may have been intimidated by his relatively high rep. The supposed "inefficiency" of the `sorted` function surely should not be a serious consideration for such small list sizes. Readability is much more important than such a tiny improvement in efficiency. You felt the `sorted` solution was clearer, and I agree with you. – John Y Sep 17 '19 at 22:13
  • @JohnY In what way is `collate(range(0, 10, 2), (5, 7))` less clear than `sorted(tuple(range(0, 10, 2)) + (5, 7))`? – blhsing Sep 17 '19 at 22:16
  • @blhsing If you want to update the answer shortly by comparing the execution times of the two solutions, so we can discuss them. What are you saying? – Massifox Sep 17 '19 at 22:28
  • And Massifox, your `sorted` solution is faster than @blhsing's by about an order of magnitude, according to *their own timings* (see comments on their answer). – John Y Sep 17 '19 at 22:33
  • Rolled back the edit since using the `sorted` function implemented in C is the most efficient approach for the OP's use case with small lists. – blhsing Sep 17 '19 at 22:37
  • Yes, I am now publishing tests to compare the 3 solutions (my two solutions and the one based on collate) – Massifox Sep 17 '19 at 22:41
  • My only nitpick with this answer as it stands is that the `sorted` solution isn't what I'd personally call "equivalent", because of algorithmic differences. Yes, they produce the same output, but so does the literal `[0, 2, 4, 5, 6, 7, 8]`, which is fastest of all. – John Y Sep 17 '19 at 22:46
  • For those with insufficient rep to see it, @blhsing deleted their answer, which mentioned the useful [`more_itertools`](https://pypi.org/project/more-itertools/) package, and specifically the [`collate`](https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collate) function. – John Y Sep 17 '19 at 23:12
  • Thanks @JohnY! I updated my answer by adding comparisons on execution times. I hope I have done a good job. – Massifox Sep 17 '19 at 23:22
1

To clarify, here are some relevant comments:

if I need to parse 1 through 1000 with exceptions of increments in between, is there a way other than specifying indexes?
Ex: for i in range(1, 1000, 20); I need i value of 178, 235, 650 in between. Can I do that in for loop?

The technical answer is: yes and no. Yes, because of course you can do it in a for loop. No, because there is no way around specifying the exceptions. (Otherwise they wouldn't be exceptions, would they?)

You still use a for loop, because Python's for loop is not really about indices or ranges. It's about iterating over arbitrary objects. It so happens that the simple numeric for loop that many other languages have is most directly translated into Python as a loop over a range. But really, a Python for loop is simply of the form

for x in y:
    # do stuff here

And the loop iterates over y, no matter what y is, as long as it's iterable, with x taking the value of one element of y on each iteration. That's it.

But, what you seem to be really after is a way to loop over a bunch of numbers that mostly follow a simple pattern. I would probably do it like this:

values = list(range(1, 1000, 20)) + [178, 235, 650]
for i in sorted(values):
    print(i)

Or, if you don't mind a longer line:

for i in sorted(list(range(1, 1000, 20)) + [178, 235, 650]):
    print(i)
John Y
  • 14,123
  • 2
  • 48
  • 72
0

You can give the for loop a list:

for i in [2, 4, 5, 6, 7, 8]:

You could also create a custom iterator if it's following an algorithm. That's described in another answer here: Build a Basic Python Iterator.

John Bayko
  • 746
  • 4
  • 7