in the process of learning python, stuck at understanding this piece of code. It works, and the book required me to test it, which I have successfully, but cannot understand how the first index ends up in the code.
def double_preceding(values):
"""(list) -> NoneType
Replace each item in the list with twice the value of the preceding item, and replace the first item with 0.
>>> L = [l , 2 , 3]
>>> double_preceding(L)
>>> L
(0, 2, 4]
"""
if values != []:
temp = values[0]
values[0] = 0
for i in range(1, len(values)):
double = 2 * temp
temp = values[i]
values[i] = double
The range starts from index 1, which would skip index 0, so how does the output of 2 get in there(from the doctest)? That would mean value 1 was doubled, but the range skips index 0...?
And another question, doesn't values[0] = 0 change the value in [0] to 0? How did the value "1" end up being doubled in the output list?
It's such simple code, but has made me lose my mind.
Thanks in advance! Appreciate your time
This is an interesting loop, and I will walk you through how it works.
The statement if values != []: checks if the loop is empty. and if not, it proceeds.
The statement temp = values[0] stores the original value of values[0] in temp. This is how the program knows to double the 1. So in our example, the value of temp would be 1.
The next step values[0] = 0 sets the value of the first element to 0, but we still know what the original value of the array was, since we stored it in temp.
Now we start the loop. The loop goes from 1 all the way to the end of the loop.
The variable double holds the value of temp multiplied by 2. So in our example, since temp is 1, double holds 2.
Now the statement temp = values[i] would store the current iteration value of the loop in temp. In our example, we would be at the first iteration, so since values[1] is 2, temp has 2 in it.
Finally, the statement values[i] = double stores the value of double in the array. currently, we are at index 1, and since double is 2, that is what that index will have.
We can loop through this sequence again. Currently, our array has {0, 2, 3}. In the next iteration of the for, double is temp*2. Since temp was 2, double is 4. Now that value of double is stored in the second index of the array. The line temp = values[i] would store 4 in temp, but it doesn't matter since the loop is finished, since the length of the array is 3, and the loop only repeats when i < 3.
When we are finished the final array is {0, 2, 4}.
def double_preceding(values):
"""(list) -> NoneType
Replace each item in the list with twice the value of the preceding item, and replace the first item with 0.
>>> L = [1 , 2 , 3]
>>> double_preceding(L)
>>> L
(0, 2, 4]
"""
if values != []:
temp = values[0] # temp is 1 now
values[0] = 0 #sets 1 to 0
for i in range(1, len(values)): # start at second item in array and go until end
double = 2 * temp # double it by the last (which is stored in temp)
temp = values[i] # temp is now equal to the item we just doubled (the "preceding item")
values[i] = double # change the value of the item at the index so it is actually the doubled item
Related
take an array and find an index i where the sum of the integers to the left of i is equal to the sum of the integers to the right of i. If there is no index that would make this happen, return -1.
Why this code doesn't work all cases.
why it is wrong ,
def find_even_index(arr):
i=1
size=len(arr)
sum_left=0
sum_right=0
for i in range(size):
sum_right=sum(arr[i+1:]) #sum of all elements in right of i
sum_left=sum(arr[:i-1] ) #sum of all elements in left of i
if(sum_right==sum_left):
return i
return -1
There are a few logical errors in your program. Firstly you declare i prior to the for loop, you should do it within the range() function itself, as the value of i resets on iterating it over the for loop. It should be like this:
for i in range(1, size):
Also you are not doing correct slicing, remember that when you slice any list by giving start and end index, it ignores the end value, and only slices till end - 1.
For example,
>>> a = [10,20,30,40,50,60]
>>> print(a[2:4]) # this will print the 2nd and 3rd element only, ignoring the 4th one
[30,40]
Also, if value of i starts from 0, arr[:i-1] will return whole array at once, so loop will break at first iteration.
For example,
>>> a = [10,20,30,40,50]
>>> print(a[0:])
[10,20,30,40,50]
>>> print(a[:-1]) # index -1 refers to the last element of array
[10,20,30,40,50]
And you are using the return -1 statement within the for loop, so the loop will break in the first iteration itself, you should do it outside the for loop.
Now your formatted code should look like this:
def find_even_index(arr):
size = len(arr)
sum_left = 0
sum_right = 0
for i in range(1, size): # value of i initially set here as 1
sum_right = sum(arr[i+1:])
sum_left = sum(arr[:i]) # corrected the slicing
if(sum_right == sum_left):
return i
return -1 # return method indented outside the for loop
Hope this answer helps! :)
If you can't understand the reasoning even now, you can read about the topics here:
Negative Slicing
Python range
I get an error on occurence[j] = 0. I do not really understand the origins of this error in my code, as it is of length dna, because I append at the top of the code len(dna) zeroes and then I assign some value to the same list occurence in my nested loop, where j can only reach the value of len(dna).
for i in range(len(dna)):
occurence.append(0)
print(f"{len(dna)}")
print(f"{len(occurence)}")
#Calculating consecutive sequences and creating a 2D array occurence...
for i in types:
for j in range(len(dna)):
if (dna[j:j+len(i)] != i):
occurence[j] = 0
else:
space = len(i)
while(dna.find(i, space+len(i)) != -1):
index = dna.find(i, space+len(i))
space = space + len(i)
if (index == len(i)):
occurence[j] += 1
for k in range(len(occurence)):
maximum = 0
if(occurence[k] > maximum):
maximum = occurence[k]
counts.append(maximum)
maximum = 0
occurence.clear()
At the end of the first iteration over types, you call occurence.clear(), which will result in occurence being an empty list. Then, when you try to access occurence[j] on the second iteration, this throws an IndexError since the list is empty.
I think you instead want to initialize your list inside the for i in types loop, e.g.:
for i in types:
occurence = [0] * len(dna)
for j in range(len(dna)):
...
You would then not need to call the clear method on your list, since it would be redefined as a list of zeroes on each iteration.
I need this function to return the sum of the elements located at the odd indices.
And that's what I have right now:
def getSumOdds(aList):
for element in aList:
if element % 2 == 1:
Since you are doing for element in aList, element % 2 == 1 will check if each element is odd, not if its index is odd.
What you can do is this:
value = 0
for index in range(len(aList)):
if index % 2 == 1:
value += aList[value]
This goes through all of the indices, and if it's odd, adds the element at that index to the accumulator.
That method is fairly easy to understand; however, it goes through an unnecessary number of elements:
value = 0
for index in range(1, len(aList), 2):
value += aList[index]
range(x, y, z) generates all elements counting up from x up to but not including y, counting by z. This starts at 1 and takes every 2 elements.
This is rather long though, and can be shortened to the following:
value = sum(aList[index] for index in range(1, len(aList), 2))
Using list slicing, where aList[start:end:jump] gives every jump-th element starting from start up to end (implicity the very end), you can do the following:
value = sum(aList[1::2])
This sums every second element starting from the first.
If your input is not necessarily indexable (that is, it's iterable but cannot use [index] syntax, such as a set, range, map, etc), you can do:
value = sum(val for index, val in enumerate(anIter) if index % 2 == 1)
This sums every value where the index is odd by getting the index and value for each enumerated value. The enumerate function is an iterable which returns (0, a[0]), (1, a[1]), ..., (len(a) - 1, a[-1]).
Using the code you've already started, enumerate is probably the function you want which returns the count and the values in the list. Then we filter the odd indices and add them to a variable for the sum, as you had already done:
def getSumOdds(aList):
total = 0
for index, element in enumerate(aList):
if index % 2 == 1:
total += element
return total
l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print getSumOdds(l) # 20 (odd indices: 2+4+6+8=20)
Just you can use a list slice list[1::2]
or function
def getSumOdds(aList):
return sum(aList[1::2])
if i run this code:
def numbers_in_lists(string):
next_number = 0
count = 0
final_list = []
while count < len(string):
number = int(string[count])
if number > next_number:
final_list.append(number)
new = []
next_number = number
else:
new.append(number)
if new not in final_list:
final_list.append(new)
count += 1
print final_list
#testcases
string = '543987'
result = [5,[4,3],9,[8,7]]
print repr(string), numbers_in_lists(string) == result
i get this answer:
'543987' [5, [4, 3], 9, [8, 7]]
False
but if i put the number variable outside of the while loop like this:
def numbers_in_lists(string):
next_number = 0
count = 0
final_list = []
number = int(string[count])
while count < len(string):
if number > next_number:
final_list.append(number)
new = []
next_number = number
else:
new.append(number)
if new not in final_list:
final_list.append(new)
count += 1
print final_list
i get this answer:
'543987' [5, [5, 5, 5, 5, 5]]
False
I am a beginner and i have looked everywhere for the explanation as to why the number variable behaves differently outside of the loop? Also, why are the other variables ok outside of the loop but not number variable?
Any help would be much appreciated. Thankyou. :)
You declare number as
number = int(string[count])
So when
count = 0
This means that number will be the first character of your string (position [0]) converted to int. If you do not update number within your loop, it will never change. In your first version of the code, number is in your while loop.
while count < len(string):
number = int(string[count])
# more code
count += 1
Note at the end of the loop, you increment count. This means at the beginning of the next iteration, count now is incremented, so number will take on the value of the next character in your string.
A common misconception for beginners is that a variable is somehow "bound" to another variable. For example:
>>> a = 5
>>> b = a
>>> b
5
>>> a = 7
>>> b
5
See, even though a changed values from 5 to 7, b doesn't change because it is not "bound" to that variable in any way. The line b = a means "take the value from a and assign it to b" after that, b doesn't care what happens to a.
The number = int(string[count]) uses the value of count assigned outside the loop which is the first character index 0, it will never change outside the loop, so you will always get the original value appended which is the first character.
Using number = int(string[count]) inside the loop you are reassigning number to the value of the element at index count each time so you get a different part of the input string appended each time.
Once you enter the while loop any values you change that were defined outside of the loop will not change outside that loop, you must reassign the value inside the loop.
I get that you are a beginner. Here is a rewrite that should help you with some useful python idioms and constructs:
def numbers_in_lists(string):
numbers = [int(c) for c in string]
next_number = -1
final_list = []
for number in numbers:
if number > next_number:
final_list.append(number)
final_list.append([])
next_number = number
else:
final_list[-1].append(number)
final_list = [x for x in final_list if x != []]
print final_list
First, be suspicious when you have a while-loop and a counter. You can replace it with a for-loop that iterates over all the items.
Since this code operates on integers, not characters in a string, let's translate them all at once using a list comprehension:
numbers = [int(c) for c in string]
This creates a list that has all the characters translated to integers.
By setting next_number = -1, we ensure that all numbers in numbers will be greater. In particular, the first element will be greater.
When we find a number that exceeds our last maximum, next_number, we add number and a bucket to put succeeding numbers less than number into:
if number > next_number:
final_list.append(number)
final_list.append([])
next_number = number
That means when we add 5, final_list looks like this:
[5, []]
And when number is 4 and 3, they get added properly:
[5, [4]]
[5, [4, 3]]
How does that happen? This code does it:
else:
final_list[-1].append(number)
Here we do not append to final_list; we append to the last list in final_list, final_list[-1].
But what if our sequence was just 5? Then at the end of the loop, final_list would look like this:
[5, []]
In that case, we want to trim out all empty lists, so we do it like this:
final_list = [x for x in final_list if x != []]
This says, "make final_list into a list that has all elements of final_list except those that are empty lists."
Good luck with learning python. I hope you find it rewarding.
It is a homework question that I am stuck on:
Your classmate claims to have written a function that replaces each value in a list with twice the preceding value (and the first value with 0). For example, if the list [1, 3, 7, 11] is passed as a parameter, the function is supposed to return [0, 2, 6, 14] -- Note: 22 is not part of the output. Here's the code:
def double_preceding(values):
if (values != []):
temp = values[0]
values[0] = 0
for i in range(1, len(values)):
values[i] = 2 * temp
temp = values[i]
Analyse this function and rewrite it so that it works as intended.
I couldn't even follow what the code was doing, can someone explain to me please
Here's an explanation of the given code, and the errors I see within it:
def double_preceding(values):
if (values != []): # this if checks for an empty list, to avoid exceptions
temp = values[0]
values[0] = 0 # but this line is not indented, so you may get an IndexError anyway
for i in range(1, len(values)): # this loops over all indexes but the first (0)
values[i] = 2 * temp # this replaces the current value with double temp
temp = values[i] # this line however saves the same doubled value as the new temp
So, the two errors I see is incorrect handling of empty lists, and a logic error in the assignment code that will cause the loop to replace values after the first with the list's original first value times successive powers of two.
A good way to solve the second issue is to do both of the assignments in the loop with a single statement. This is a neat thing that Python can do that many other languages cannot. Here's what a basic version would look like:
values[i], temp = temp*2, values[i]
The commas are the key things to pay attention to. The one on the right side of the assignment makes a tuple out of temp*2 and values[i]. The comma on the left hand side tells Python to unpack the tuple being assigned into the variables values[i] and temp. And the two parts are evaluated in that order (first the expression on the right side, then the unpacking and assignments). This means that the "old" values of temp and values[i] are used to build the tuple and it doesn't matter that they're both reassigned later.
If we're doing the assignments that way, we can solve the empty list situation elegantly too. Rather than treating the first value specially and needing a check to make sure values[0] is a valid expression, why not just set temp to 0 at the start and let the loop handle the first value as well as the later ones? Here's a fully fixed function:
def double_preceeding(values):
temp = 0
for i in range(len(values)): # loop over all indexes, not skipping the first
values[i], temp = temp*2, values[i]
The loop will do nothing if values is an empty list, since len([]) is 0 and range(0) is empty itself.
Example output:
>>> L=[]
>>> double_preceeding(L)
>>> L
[]
>>> L=[1, 3, 7, 11]
>>> double_preceeding(L)
>>> L
[0, 2, 6, 14]
If I guessed the indentation of the program correctly. See comments below:
Code:
def double_preceding(v):
values = v[:] # Make a copy of the list passed as argument
if (values != []): # If list is not empty, store the first value in 'temp'
temp = values[0]
else:
return
v[0] = 0 # Set the first value of the list as '0' (as the problem says it)
for i in range(1, len(values)): # Iterate 'n - 1' times, where n is the length of the list
v[i] = 2 * temp # Set the corresponding value to twice the precedent (the precedent is stored in 'temp')
temp = values[i]
Test:
v = [1, 3, 7, 11]
double_preceding(v)
print v
Output:
[0, 2, 6, 14, 22]