Change range start in Python 'for loop' for each iteration - python

I'm a novice and learning python (using 2.7). I'm attempting some simple scripts to test how python handles different types of loops.
My question is: can python change the starting point of the "range" function on each iteration if the start point is assigned to a variable? Here is an example of my code:
def build(n, period):
n2 = n
for digit in range(int(n2), 20):
print "At the top i is %d" % n
digit += period
numbers.append(digit)
print "Numbers now:", numbers
print "At the bottom i is %d" % digit
print "The Numbers:"
n2 += period
for num in numbers:
print num
key = raw_input("Press 1 to call function \"build\", press any other key to quit.")
if key == "1":
i = raw_input("What integer is our start value?")
amt = raw_input("What is the common difference?")
numbers = [int(i)]
build(int(i),int(amt))
else:
quit()
I tried to use a second local variable 'n2' inside the function so I could keep the initial value of 'n' constant and then redefine the range for each iteration. The very first number in the appended list moves by the common difference but after that it always steps by +1 integer. I can easily make this happen with a 'while' loop but curious if a 'for' loop can be used to accomplish this?

range creates a fixed list the moment you call that function at the beginning of your for loop. You can think of the top of the for loop as assigning n2 to the next element of that list no matter what you do inside the loop. If you want to change the period of the range, use the third argument:
range(n, 20, period)
will move in steps of size period through the range instead of steps of size one.

It won't work in a way you expect. Expression range(int(n2), 20) gets evaluated only one time in the beginning of for-loop. You can't change the scope of a for-loop that way.
What you can modify is a step parameter in range function, but it does not change your starting point - it only defines what is the next element in the iteration process.

Related

Why isn't my function for getting the smallest value from a list working

I made a function for getting the smallest value from a list but it is not working properly and returning the wrong value. It prints out 5. What is wrong with it?
def finding_least_num(a_list):
first = 0
for i in a_list:
while first < len(a_list)-1:
if i < a_list[first]:
min_num = a_list[first]
first = first + 1
first = 0
print(min_num)
finding_least_num([3,7,5,2,9])
I am not sure why there are a lot of inner if and while statements. #Swishy has diagnosed the problem you are sticking with correctly. if you are insisting on not using min function, you can try code below:
def finding_least_num(a_list):
first = float('inf')
for value in a_list:
if value < first:
first = value
return first
which results in 2 in the case of using [3,7,5,2,9] as an input for the function.
Note that float("inf") is referring to the maximum number available in python.
if i < a_list[first]:
This is your problem, this instead needs to be:
if min_num > a_list[first]
It doesn't work because you are not checking against the lowest number you have saved already in min_num. You also need to initialize min_num to the first record in the array.
def finding_least_num(a_list):
first = 0
for i in a_list:
print("testing i = ", i)
while first < len(a_list)-1:
print("first = ", first)
if i < a_list[first]:
print ("checking if i < ", a_list[first])
print("i", i)
min_num = a_list[first]
print("minimum number here is ", min_num)
first = first + 1
first = 0
print(min_num)
finding_least_num([3,7,5,2,9])
I rewrote your code with comments and run it. Copy this into your interpreter and you will see the error and the reason why you received 5 each time. You will realize that your first for loop takes the first index of the array you passed to the function and tests it against the second while loop (let's call it chosen index). For each cycle the selected index is tested if it is less than the chosen index. If true set the minimum number as the chosen index. If you test the new code I have up there with the prints you will discover the last minimum number recorded is what was returned after the two loops completed. Python has many methods to help in these instances. For example min() is a very good way to simplify things. Another good way is to sorted() the array and return the first or last index depending on the order of the sorting.

Why does the range(first_num, second_num) not include second_num? [duplicate]

This question already has answers here:
Why are slice and range upper-bound exclusive?
(6 answers)
Closed last month.
>>> range(1,11)
gives you
[1,2,3,4,5,6,7,8,9,10]
Why not 1-11?
Did they just decide to do it like that at random or does it have some value I am not seeing?
Because it's more common to call range(0, 10) which returns [0,1,2,3,4,5,6,7,8,9] which contains 10 elements which equals len(range(0, 10)). Remember that programmers prefer 0-based indexing.
Also, consider the following common code snippet:
for i in range(len(li)):
pass
Could you see that if range() went up to exactly len(li) that this would be problematic? The programmer would need to explicitly subtract 1. This also follows the common trend of programmers preferring for(int i = 0; i < 10; i++) over for(int i = 0; i <= 9; i++).
If you are calling range with a start of 1 frequently, you might want to define your own function:
>>> def range1(start, end):
... return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Although there are some useful algorithmic explanations here, I think it may help to add some simple 'real life' reasoning as to why it works this way, which I have found useful when introducing the subject to young newcomers:
With something like 'range(1,10)' confusion can arise from thinking that pair of parameters represents the "start and end".
It is actually start and "stop".
Now, if it were the "end" value then, yes, you might expect that number would be included as the final entry in the sequence. But it is not the "end".
Others mistakenly call that parameter "count" because if you only ever use 'range(n)' then it does, of course, iterate 'n' times. This logic breaks down when you add the start parameter.
So the key point is to remember its name: "stop".
That means it is the point at which, when reached, iteration will stop immediately. Not after that point.
So, while "start" does indeed represent the first value to be included, on reaching the "stop" value it 'breaks' rather than continuing to process 'that one as well' before stopping.
One analogy that I have used in explaining this to kids is that, ironically, it is better behaved than kids! It doesn't stop after it supposed to - it stops immediately without finishing what it was doing. (They get this ;) )
Another analogy - when you drive a car you don't pass a stop/yield/'give way' sign and end up with it sitting somewhere next to, or behind, your car. Technically you still haven't reached it when you do stop. It is not included in the 'things you passed on your journey'.
I hope some of that helps in explaining to Pythonitos/Pythonitas!
Exclusive ranges do have some benefits:
For one thing each item in range(0,n) is a valid index for lists of length n.
Also range(0,n) has a length of n, not n+1 which an inclusive range would.
It works well in combination with zero-based indexing and len(). For example, if you have 10 items in a list x, they are numbered 0-9. range(len(x)) gives you 0-9.
Of course, people will tell you it's more Pythonic to do for item in x or for index, item in enumerate(x) rather than for i in range(len(x)).
Slicing works that way too: foo[1:4] is items 1-3 of foo (keeping in mind that item 1 is actually the second item due to the zero-based indexing). For consistency, they should both work the same way.
I think of it as: "the first number you want, followed by the first number you don't want." If you want 1-10, the first number you don't want is 11, so it's range(1, 11).
If it becomes cumbersome in a particular application, it's easy enough to write a little helper function that adds 1 to the ending index and calls range().
It's also useful for splitting ranges; range(a,b) can be split into range(a, x) and range(x, b), whereas with inclusive range you would write either x-1 or x+1. While you rarely need to split ranges, you do tend to split lists quite often, which is one of the reasons slicing a list l[a:b] includes the a-th element but not the b-th. Then range having the same property makes it nicely consistent.
The length of the range is the top value minus the bottom value.
It's very similar to something like:
for (var i = 1; i < 11; i++) {
//i goes from 1 to 10 in here
}
in a C-style language.
Also like Ruby's range:
1...11 #this is a range from 1 to 10
However, Ruby recognises that many times you'll want to include the terminal value and offers the alternative syntax:
1..10 #this is also a range from 1 to 10
Consider the code
for i in range(10):
print "You'll see this 10 times", i
The idea is that you get a list of length y-x, which you can (as you see above) iterate over.
Read up on the python docs for range - they consider for-loop iteration the primary usecase.
Basically in python range(n) iterates n times, which is of exclusive nature that is why it does not give last value when it is being printed, we can create a function which gives
inclusive value it means it will also print last value mentioned in range.
def main():
for i in inclusive_range(25):
print(i, sep=" ")
def inclusive_range(*args):
numargs = len(args)
if numargs == 0:
raise TypeError("you need to write at least a value")
elif numargs == 1:
stop = args[0]
start = 0
step = 1
elif numargs == 2:
(start, stop) = args
step = 1
elif numargs == 3:
(start, stop, step) = args
else:
raise TypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
i = start
while i <= stop:
yield i
i += step
if __name__ == "__main__":
main()
The range(n) in python returns from 0 to n-1. Respectively, the range(1,n) from 1 to n-1.
So, if you want to omit the first value and get also the last value (n) you can do it very simply using the following code.
for i in range(1, n + 1):
print(i) #prints from 1 to n
It's just more convenient to reason about in many cases.
Basically, we could think of a range as an interval between start and end. If start <= end, the length of the interval between them is end - start. If len was actually defined as the length, you'd have:
len(range(start, end)) == start - end
However, we count the integers included in the range instead of measuring the length of the interval. To keep the above property true, we should include one of the endpoints and exclude the other.
Adding the step parameter is like introducing a unit of length. In that case, you'd expect
len(range(start, end, step)) == (start - end) / step
for length. To get the count, you just use integer division.
Two major uses of ranges in python. All things tend to fall in one or the other
integer. Use built-in: range(start, stop, step). To have stop included would mean that the end step would be assymetric for the general case. Consider range(0,5,3). If default behaviour would output 5 at the end, it would be broken.
floating pont. This is for numerical uses (where sometimes it happens to be integers too). Then use numpy.linspace.

Connecting last and first value of list

I have a list of numbers, say list= [0,1,0,1,0]. I have to check for each element i in list whether its equal to its previous and the element after it and if it is, change its value to the previous one. I presented this task as:
i-1=x
i+1=y
for i in list:
if x=y and i!=x
i=x
elif x!=y
i=i
But I need to connect the first and the last element in list, so that if i=list[0]: x=list[4] (and y=list[2]). My idea was to define another loop like this:
if i=list[0]
x=list[4]
But when i execute the program it writes that x=list[0] is "syntax error: invalid syntax". First, I am not quite sure why is this: I was thinking that I didn't define x (but I did!), or I maybe didn't define it in the right place (I am not sure where I should define it then). On the other hand, is this the way to connect last and first value of list?
Here is a code that does exactly what you asked.
test=[0,1,0,1,0] # don't use 'list' as variable name
for j in range(len(test)): # j=index of current
i = (j-1) % len(test) # i=index of previous
k = (j+1) % len(test) # k = index of next
if test[i] == test[k]: # equal to previous and next
test[j] = test[i] # change to previous
print(test)
Try this:
i-1=x
i+1=y
for i in list:
if x==y and i!=x :
i=x
elif x!=y
i=i
= means assignment whereas == means equality test. And the colon : in python defines the beginning of a scope (indented) so you need :' after anif` to define its scope block.

How do I double my step variable for each for-loop iteration in Python 3?

I'm new to Python, and I'm playing around with recursive functions just for practice.
I made the following algorithm which takes a number as x and halves it until it is equal to 1. n is the number of times x has been halved.
def binary(x, n = 0):
print(n,":",x)
x = x // 2
n += 1
if x > 0:
binary(x, n)
return x
return x
I'm trying to make a loop that will call binary() with multiple values for x. I want my step to be doubled each iteration. I have it working with a while loop like the one below.
i = 1
while i < 1000000000:
print("when x is", i, ":")
binary(i)
i += i
For some reason though, I can't seem to achieve the same thing with a For loop. Here's what I have now.
for i in range(1,1000):
print("when x is", i, ":")
binary(i)
i += i
In the code above, i += i does not seem to effect the i in my header. I know that range() takes a third parameter called step, but I've tried this:
for i in range(1,1000, i += i):
# statements
This gives me a name error, and says "i is not defined".
Most of my experience with programming is in JavaScript and C#. In both of those languages I wouldn't of had any trouble doing this.
How would I get this to work in a For loop using Python 3?
The third parameter of range is indeed step. But you should use it with a computed value like:
for i in range(1,1000,2):
#statements
The reason why your i += i didn't worked is because, under the hood, the for-loop is executing something similar to i = next(...) at the end of an iteration, overiding your last increment.
Edit
You can achieve the desired effect using a generator, but it kinda kills the "trying to avoid while-loops" thing:
def myrange(start, stop):
i = start
while i < stop:
yield i
i += i
for i in myrange(1, 1000):
print(i)
Anyway, while-loops are perfectly valid constructs and I’d personnally go with one in this case. Do not forget that the for-loop has a rather different semantic in python than in both languages you’re used to. So trying to use a for-loop because you are able to do so with javascript seems like a bad idea if all what you need is a while-loop.
range can step by a fixed amount, but not a variable amount. Use a while-loop to increment i by i:
i += i
You could replace the while-loop with an iterator, such as:
import itertools as IT
for i in (2**i for i in IT.count()):
if i >= 1000000000: break
print("when x is", i, ":")
binary(i)
but I don't think this has any advantage over a simple while-loop.
If all you're doing is doubling i, then why not just raise it to the power?
for p in range(int(1000000000**0.5)):
print(binary(2**p)

Why is this while statement looping infinitely?

I am using Python 2.7 in Windows PowerShell.
def loop(loop):
i = 0
numbers = []
while i < loop:
print "At the top i is %d" % i
numbers.append(i)
i += 1
print "Numbers now: ", numbers
print "At the bottom i is %d" % i
value = raw_input("Choose the loop value:\n>")
print value
loop(value)
When I enter 6 as the input for value, loop() turns into an infinite loop.
Any idea what is going wrong?
The result of your raw_input, the variable value (passed to loop in your function) is a string. You are comparing this to i, an integer. In Python 2.x, all integers are less than all strings, so i < loop is always true no matter how big i gets.
Convert your input to an integer to make the comparison work:
value = int(raw_input("Choose the loop value:\n>"))
(And I'd also suggest not naming your function's argument the same as the function itself; it's just confusing.)
You have to put your raw_input in int().
replace:
value = raw_input("Choose the loop value:\n>")
on:
value = int(raw_input("Choose the loop value:\n>"))
OR you can just change:
while i < loop:
to
while i < int(loop):
I am sure you are actually running loop('6') rather than loop(6).
because value is a string
you should call your function like this
loop(int(value))

Categories

Resources