How do I use a C-style for loop in Python? - python

I want to use the traditional C-style for loop in Python. I want to loop through characters of a string, but also know what it is, and be able to jump through characters (e.g. i =5 somewhere in the code).
for with range doesn't give me the flexibility of an actual for loop.

In C:
for(int i=0; i<9; i+=2)
{
dosomething(i);
}
In python3:
for i in range(0, 9, 2):
dosomething(i)
You just express the same idea in different languages.

There is no simple, precise equivalent of C's for statement in Python. Other answers cover using a Python for statement with a range, and that is absolutely what you should do when possible.
If you want to be able to modify the loop variable in the loop (and have it affect subsequent iterations), you have to use a while loop:
i = 0
while i < 7:
if someCondition(i):
i = 5
i += 1
But in that loop, a continue statement will not have the same effect that a continue statement would have in a C for loop. If you want continue to work the way it does in C, you have to throw in a try/finally statement:
i = 0
while i < 7:
try:
if someCondition(i):
i = 5
elif otherCondition(i):
continue
print 'i = %d' % i
finally:
i += 1
As you can see, this is pretty ugly. You should look for a more Pythonic way to write your loop.
UPDATE
This just occurred to me... there is a complicated answer that lets you use a normal Python for loop like a C-style loop, and allows updating the loop variable, by writing a custom iterator. I wouldn't recommend this solution for any real programs, but it's a fun exercise.
Example “C-style” for loop:
for i in forrange(10):
print(i)
if i == 3:
i.update(7)
Output:
0
1
2
3
8
9
The trick is forrange uses a subclass of int that adds an update method. Implementation of forrange:
class forrange:
def __init__(self, startOrStop, stop=None, step=1):
if step == 0:
raise ValueError('forrange step argument must not be zero')
if not isinstance(startOrStop, int):
raise TypeError('forrange startOrStop argument must be an int')
if stop is not None and not isinstance(stop, int):
raise TypeError('forrange stop argument must be an int')
if stop is None:
self.start = 0
self.stop = startOrStop
self.step = step
else:
self.start = startOrStop
self.stop = stop
self.step = step
def __iter__(self):
return self.foriterator(self.start, self.stop, self.step)
class foriterator:
def __init__(self, start, stop, step):
self.currentValue = None
self.nextValue = start
self.stop = stop
self.step = step
def __iter__(self): return self
def next(self):
if self.step > 0 and self.nextValue >= self.stop:
raise StopIteration
if self.step < 0 and self.nextValue <= self.stop:
raise StopIteration
self.currentValue = forrange.forvalue(self.nextValue, self)
self.nextValue += self.step
return self.currentValue
class forvalue(int):
def __new__(cls, value, iterator):
value = super(forrange.forvalue, cls).__new__(cls, value)
value.iterator = iterator
return value
def update(self, value):
if not isinstance(self, int):
raise TypeError('forvalue.update value must be an int')
if self == self.iterator.currentValue:
self.iterator.nextValue = value + self.iterator.step

for i in range(n):
...is the Python equivalent of the C...
for (i = 0; i < n; i++){
Or well, you can use:
for i in range(a, n, s):
...which is equivalent to...
for (i = a; i < n; i+=s){

I provide the following entirely facetious solution by way of protest. Note that 'break' and 'continue' will not work. Also note that the loop body must not be indented.
class For:
def __init__(self, **loop_vars):
self.loop_vars = loop_vars
def __call__(self, arg):
if not hasattr(self, 'condition'):
self.condition = arg
return self
if not hasattr(self, 'update'):
self.update = arg
return self
while eval(self.condition, self.loop_vars, self.loop_vars):
exec arg in self.loop_vars
exec self.update in self.loop_vars
For(i = 1, j = 1)('i * j < 50')('i += 1; j += 1')('''
print i, j
''')

You can do the following, given an array a:
for i in range(len(a)):
a[i] = i
That's the closest Python can get to C-style loops.
You can also give the range command more arguments; for example,
for i in range(2, len(a), 3)
will start at i = 2, and increment it by 3 as long as the result is less than len(a).

The Python for loop always has foreach semantics. You can, however, do this:
for i in xrange(10):
print i
This is very much like a C for loop. xrange (or range, as it was renamed in Python 3) is a constructor for a Python object that iterates through a range of numbers. See the docs for more information.

For all the ones going "Why?", here's an example where a C-style loop would be helpful:
remaining_retries = 3
for i in range(num_runs):
success = do_run()
if not success and remaining_retries > 0:
i = i - 1
remaining_retries = remaning_retries - 1

The top answer here is fundamentally incorrect in any true for-looping situation. And the second answer does address the issue, but over complicates things (a C-loop is possible).
Bottom Line, Up Front (BLUF):
## The code:
i = -1
length = 100
while i < length - 1:
i += 1
# ...
## With comments:
# start from -1 to enable continues (adapt as needed)
i = -1
length = 100
while i < length - 1: # the loop
# increment # 1st line to duplicate C loop and allow normal continues
i += 1
# proceed as if in scope of: for(i=0; i < len(orig_line); i++)
Discussion:
For example, in writing a loop in a process that genuinely deserves a loop (such as across a string or for some algorithm's internal method), it is often desirable to have a loop and slide structure:
for (int i = 0; i < 100; i++){
char c = my_string[i];
if (c != '(') continue;
while (c != ')') {
// do whatever, collect the content etc.
i++
c = my_string[i];
}
// do something else here
}
HOWEVER, in python:
for i in range(100):
c = my_string[i]
if c != '(':
// do whatever
continue
while c != ')':
// collect the content
i += 1
c = my_string[i]
// do something else here
Generates the ith value from the range yield, and therefore does not respect modifications to i from within the loop!
Opinion:
I can't think of any other language consistent with this design choice other than the shell languages using the seq tool. But we are employed to write python to craft applications, not scripts. That makes this design choice annoying. Bear in mind that languages die due to the accumulation of annoying choices.
It also means that python doesn't actually have a for loop; it has iterators and recognizes what we are calling a for loop as a grammatical construct for iterating.
Python is getting fast now as technique and implementation improves. All of us are using it for a variety of problems. Perhaps it is time to give python a real for-loop.

Related

Binary Search in Python - Iterative Method

so I'm trying to learn programming myself in Python language and was confused implementing Binary Search in Python. So here's what I have done
list = [3,6,8,12,14,17,25,29,31,36,42,47,63,55,62]
key = 42
print(list)
def high(sorted_list):
max_index = len(sorted_list)
return max_index
def low(sorted_list):
start_index = 0
return start_index
def mid(sorted_list):
mid_index = ( low(sorted_list) + (high(sorted_list) - low(sorted_list)) ) // 2
return mid_index
for x in range(4):
if list[mid(list)] < key:
list = list[mid(list)+1:]
elif list[mid(list)] < key:
list = list[mid(list)-1:]
print(list)
I know I should not keep a range number in for loop but I know it will only make 4 comparisons in this example so I hardcoded it. But when I run it, it splits the list only once and keep on printing the second half of the list. Output image:
Ok, I tried your code and had to do a few corrections:
The while loop had to be modified (you knew that)
There wasn't a check for the case where the key is found (see comments)
There was a typo, < instead of > (see comments)
In the same line, the list partition was wrong
The low function was useless (returning a constant value) (see comments)
The high function was useless too (simply returning the value from another function)
The mid function was more complicated than needed (it boiled down to taking a value, then adding and subtracting zero), so it can simply take the value
Ah, btw the input list is not sorted in your example.
This is my proposal:
def mid(lst):
return len(lst) // 2
def bin_search(lst, k):
while lst:
i = mid(lst)
if lst[i] == k:
return True
if lst[i] < k:
lst = lst[i+1:]
elif lst[i] > k:
lst = lst[:i]
else:
return False
bin_search([3,6,8,12,14,17,25,29,31,36,42,47,55,62,63], 42)
True
bin_search([3,6,8,12,14,17,25,29,31,36,42,47,55,62,63], 58)
False

Nth Fibonacci in python

def fibonaci(i,memo):
if i == 0 or i == 1:
return i
if memo[i]==-1:
memo[i] = fibonaci(i-1,memo) + fibonaci(i-2,memo)
return memo[i]
def fibo(n):
a = []
return fibonaci(n,a)
print(fibo(2))
I'm a Java programmer learning python. This algorithm computes the nth fibonacci number using recursion + memoization. I don't understand why I'm seeing this error "IndexError: list index out of range" when running the program in python. Can anybody help? Thanks a ton!
As suggested in the comments to your question, there is no way memo[i]==-1 could be true.
I understand your want to test something like "if the value for fibonacci(i) has not yet been memoized", but the way Python will tell you in index is not present is certainly not by returning some magic value (like -1), but instead by raising an exception.
Looking up "EAFP" (easier to ask for forgiveness than permission) on your favorite search engine might show you why exceptions are not to be understood as errors, in python.
In addition, memoization will be preferably implemented by a dictionary, rather than a list (because dictionaries allow to map a value to any possible key, not necessarily to the next integer index value).
Without changing too much to the structure of your program, I would suggest the following :
def fibonaci(i,memo):
if i == 0 or i == 1:
return i
try:
memo[i]
except KeyError:
memo[i] = fibonaci(i-1,memo) + fibonaci(i-2,memo)
return memo[i]
def fibo(n):
a = {}
return fibonaci(n,a)
print(fibo(2))
I made few changes in your code to get nth Fibonacci no.(there might be other way too)
def fibonaci(i,memo):
if i == 0 or i == 1:
return i
if memo[i] == -1:
memo[i] = fibonaci(i-1,memo) + fibonaci(i-2,memo)
return memo[i]
def fibo(n):
a = [-1] * n
return fibonaci(n-1,a)
print(fibo(5))
print(fibo(10))
print(fibo(13))
print(fibo(57))
And output is :-
3
34
144
225851433717
You are seeing the error because you create an empty list with a = [] and then you try to look into it. You need to create a list long enough to index from zero up to n-1.
That being said, an easy way to do memoisation in Python is using the cache function from functools. This code does the memoisation for you:
from functools import cache
#cache
def fib(n):
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)
for n in range(10):
print(f"{fib(n) = }")
It isn't quite as efficient in running time, but in programmer time it is.

Make fibo faster [duplicate]

This question already has answers here:
Efficient calculation of Fibonacci series
(33 answers)
Closed 4 years ago.
I need to write a code to give a number and print me the F[number].This code is pretty slow.Any ideas for a faster code?
while True:
n=input()
if n=='END' or n=='end':
break
class Fibonacci:
def fibo(self, n):
if int(n) == 0:
return 0
elif int(n) == 1:
return 1
else:
return self.fibo(int(n)-1) + self.fibo(int(n)-2)
f=Fibonacci()
print(f.fibo(n))
I have written a bit about faster fibonacci in this post, maybe one of them is useful for you? https://sloperium.github.io/calculating-the-last-digits-of-large-fibonacci-numbers.html
Anyway. Your code is very slow because you get exponential running time calling the same subtrees over and over.
You can try a linear solution:
def fib3(n):
if n == 0:
return 0
f1 = 0
f2 = 1
for i in range(n-1):
f1,f2 = f2, f1+f2
return f2
You can use functools memoize to make it store previous values so it doesn't have to recursively call the fibonacci function. The example they list is literally fibonacci
You can use a dict to memoize the function:
class Fibonacci:
memo = {}
def fibo(self, n):
if n in self.memo:
return self.memo[n]
if int(n) == 0:
value = 0
elif int(n) == 1:
value = 1
else:
value = self.fibo(int(n) - 1) + self.fibo(int(n) - 2)
self.memo[n] = value
return value
Use dynamic programming: this prevents it calculating all the way down to 0 and 1 each time:
memory = {0:0, 1:1}
def fibo(n):
if n in memory:
return memory[n]
else:
ans = fibo(int(n)-1) + fibo(int(n)-2)
memory[n] = ans
return ans
Test:
>>> fibo(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
This is almost instantaneous.
Don't use a class; you're not gaining anything from it
Don't needlessly redefine your class each loop
Convert from str to int once, up front, rather than over and over
(If not required by assignment) Use iterative solution, not recursive
With just #1-3, you'd end up with:
def fibo(n): # Using plain function, defined once outside loop
if n < 2:
return n
return fib(n - 1) + fibo(n - 2)
while True:
n = input()
if n.lower() == 'end':
break
print(fibo(int(n))) # Convert to int only once
If you're not required to use a recursive solution, don't; Fibonacci generation is actually a pretty terrible use for recursion, so redefine the function to:
def fibo(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
which performs O(n) work rather than O(2**n) work. Memoizing could speed up the recursive solution (by decorating fibo with #functools.lru_cache(maxsize=None)), but it's hardly worth the trouble when the iterative solution is just so much faster and requires no cache in the first place.

Python for loop with modulo

Is it possible to create a python for-loop with a modulo operation? I have a ringbuffer in Python and I want to iterate the elements between the startPos and endPos indexes, where startPos can have a bigger value than endPos. In other programming languages, I would intuitively implement this with a modulo operator:
int startPos = 6;
int endPos = 2;
int ringBufferSize = 8;
for(int i = startPos, i != endPos, i = (i+1) % ringBufferSize) {
print buffer.getElementAt(i);
}
Is there a way to do this easily in Python? I only found the
for i in list:
print buffer[i]
Syntax but nothing which provides an equivalent solution to my problem.
My next approach would be to create the list in advance before iterate the indexes which are stored in the list. But is there a way to do this as a one-liner like in other programming languages by using the modulo operation directly in the for loop?
You have some ways of doing that:
As you do in "other programing languages" (i.e. C derived syntaxes), just that you basically have to write their for loop in a while form - and then you realize that C's for is just a while nonetheless:
start_pos = 6
end_pos = 2
ring_buffer_size = 8
i = start_pos
while True:
i = (i + 1) % ring_buffer_size
if i <= end_pos:
break
# your code here
Now, for the for statement, Python only has what is called "for each" - which always walks an iterable or sequence. So you can create an iterable that will yield your values -
def ring(start, end, buffer_size, increment=1):
i = start
while i != end:
yield i
i += 1
i %= buffer_size
for slot in ring(6, 2, 8):
# your code here
Note that while this second form is "bigger", it does abstract away your circular buffer logic, avoiding that hard code values get mixed with their meaning where you don't need to look at them - that is, inside the for body itself.
Note that the actual idea of for in Python is to iterate over the buffer contents itself, not and index that will lead to its contents.
So, the Python standard library includes a ready made circular buffer object already that always have its indexes normalized to 0 and (len - 1) -
just import deque from the collections module.
If you want a circular buffer with changing start and end indexes taht will wrap around and work automatically in forstatements, that is also relatively easy to do - if you don need the full functionality, just subclass list, add the start and end indexes, and make a custom implementation of its __iter__ method:
class Circular(list):
def __init__(self, content, start, end):
super(Circular, self).__init__( content)
self.start = start
self.end = end
def __iter__(self):
for i in range(self.start, self.start + len(self)):
if i % len(self) == self.end: break
yield self[i % len(self)]
And now you can use this custom container in your code:
In [22]: mylist = Circular(range(8), 6 , 2)
In [23]: for i in mylist:
...: print(i)
...:
6
7
0
1
For loops can take any iterable. As such you can create your own to do the work and drop it into the for loop. For example:
for i in [i % ring_buffer for i in range(start_pos, end_pos)]:
# Do stuff...
Or, to create an iterable directly:
for i in (i % ring_buffer for i in range(start_pos, end_pos)):
# Do stuff...
See the docs for more information about when you might want to create an iterator directly for this purpose.
use range
for i in range(0,len(a)):
#code
i=i%x
I believe you should be able to use while instead of a for loop. This should work as long as you just want to increment i by 1 each time, then calculate the mod.
Try:
i = startPos
while (i <= endPos and i == (i+1) % ringBufferSize) :
print buffer.getElementAt(x)
i = i+1

How do I increment a counter inside a while test of Python

How would you translate the following Java idiom to Python?
Comparable[] a, int lo, int hi;
int i = lo, j = hi+1;
Comparable v = a[lo];
while (a[++i] < v) if (i == hi) break;
My problem is that in the while test I cannot have a ++i or i += 1.
The problem you can't do that way in Python is a restriction of Python syntax. Let's get what does while look like from documentation:
while_stmt ::= "while" expression ":" suite
["else" ":" suite]
As You can see you must put expression before ":", while x += 1 is a statement (and statements doesn't return any value so cannot be used as a condition).
Here's what does this code look like in Python:
i += 1
while a[i] < v:
if i == hi:
break
i += 1
Though it works, it's most likely not a Python way to solve your problem. When you have a collection and you want to use indices you have to look forward to redesigning your code using for loop and enumerate built-in function.
P.S.
Anyway, direct code porting between languages with absolutely different philosophies is not a good way to go.
The Java code sets i to the index of either the first element >= a[lo], or to hi, whichever appears first. So:
v = a[lo]
for i in range(lo+1, hi+1):
if a[i] >= v:
break
If you want to iterate over all the objects in a list or any "iterable" thing, use "for item in list". If you need a counter as well, use enumerate. If you want a range of numbers use range or xrange.
But sometimes you really do want a loop with a counter that just goes up which you're going to break out of with break or return, just like in the original poster's example.
For these occasions I define a simple generator just to make sure I can't forget to increment the counter.
def forever(start=0):
count = start
while True:
yield count
count += 1
Then you can just write things like:
for count in forever():
if do_something() == some_value:
break
return count
The list class has a built-in method that does this kind of searching, but for whatever reason it only compares for equality. Of course, we can hack that:
class hax:
def __init__(self, value): self.value = value
def __eq__(self, other): return other >= self.value
a.index(hax(a[lo]), lo + 1, hi + 1)
... but please don't :)
Anyway, not only should you not be trying to port the code directly, as #Rostyslav suggests - you shouldn't really be trying to port the problem directly. There's something very strange about a Python program that uses lists in a way that would allow a problem like this to come up.

Categories

Resources