Why am I getting an infinite for loop? - python

For this python problem I am taking in one argument an int, this is the max length of a list I am going to be appending to. Starting with an int value of 1, I want to iterate through the list appending two more linear values until the max length is reached.
I am getting an infinite loop or something similar; I am not receiving any values but python is still processing.
This is the code where the infinite loop is occurring, wondering why?
Code:
def dbl_linear(n):
lst_u =[1]
while len(lst_u)<=n:
for i in lst_u:
lst_u.append(2*i+1)
lst_u.append(3*i+1)
return sorted(lst_u)
dbl_linear(10)
I looked up this example user had set a variable to another variable that contained a list before the for loop and then changed it back after the loop. Wondering why this is done and why it is useful?
other code:
d
ef dbl_linear(n):
lst_u =[1]
while len(lst_u)<=n:
new_u = lst_u
for i in lst_u:
new_u.append(2*i+1)
new_u.append(3*i+1)
new_u=lst_u
return sorted(lst_u)
dbl_linear(10)

You are adding to the list as you loop over it. A list iterator simply increments a position counter and returns the value at that index until the index doesn't exist. It doesn't keep track of the length of the list up-front. In your case, by adding more elements to the end, the iterator never reaches an index that doesn't exist.
You have several options:
Create a copy of the list first to iterate over; that copy won't grow:
def dbl_linear(n):
lst_u = [1]
while len(lst_u)<=n:
for i in lst_u[:]: # [:] returns a shallow copy
lst_u.append(2*i+1)
lst_u.append(3*i+1)
return sorted(lst_u)
Append to a separate, new list and extend the original after the loop:
def dbl_linear(n):
lst_u = [1]
while len(lst_u)<=n:
new_values = []
for i in lst_u:
new_values.append(2*i+1)
new_values.append(3*i+1)
lst_u.extend(new_values)
return sorted(lst_u)
Use a range() to produce indices; this is based on taking the length once:
def dbl_linear(n):
lst_u = [1]
while len(lst_u)<=n:
for idx in range(len(lst_u)):
i = lst_u[idx]
lst_u.append(2*i+1)
lst_u.append(3*i+1)
return sorted(lst_u)

Related

Implementing fuction that takes in array and multiplies each element by given int [duplicate]

This question already has answers here:
Trying to modify a list in Python
(3 answers)
Closed 4 years ago.
def multAll(A, k):
# takes an array of integers and an int, and multiplies each integer by the int.
for i in A:
i = i*k
return i
# test 1
A = [5,12,31,7,25]
multAll(A, 10)
print(A) # should print [50,120,310,70,250]
What am I doing wrong in multAll that isnt giving me the correct answer?
When return i happens for the first time in your function, the function stops and returns the current i.
def multAll(A, k):
return_value = []
for i in A:
i = i*k
return_value.append(i)
return return_value
Like this, a complete list return_value is created, and that list is returned.
This will give you the desired result:
def multAll(A, k):
return [i * k for i in A]
It is using the list comprehension pattern that allows you to edit each element of the list.
As written, your function should not return the given list; it should return the value of A[0] * k, the scalar 50.
Let's walk through the code for a moment, as you should have done with a print statement before posting.
for i in A:
# i is a local variable; it takes on the value of A[0], or 5
i = i*k
# i is now 50
return i
# You return 50 to the calling program -- which ignores the return value.
At this point, the function is done, gone, exited, and will execute no more until you call it again.
To get the list (you have a list, not an **array*) altered, you can change each element in place. Don't leave the function until you've processed all of the elements.
for i in range(len(A)):
A[i] *= k
return
Since you're calling the multAll function without expecting a returning value, you should update the list A in-place by updating its item values through indices:
def multAll(A, k):
for i in range(len(A)):
A[i] *= k
Numbers are not the objects, so In loop every time you get only the next value of a list. And to that value, you do that operation. You should iterate over indexes, and assign a value to a specific place in the list. You could use the range function to do that.
Another problem is that you return value after calculating the first value in the list, so you should remove one indention.

list slicing without using the slice function

Python provides slicing functionality for lists, but for this
question, you will implement your own function capable of producing
list slices (note: you cannot use the slicing operator in your
solution). The function should be called slice and take the following
three inputs in this specific order:
A list, source, which the slice will be created from. This list cannot be modified by your function.
A positive integer, start, representing the starting index of the slice you will create. If this value is not in the range [0, len(list)-1], your function should return an empty list.
A positive integer, end, representing the ending index of the slice you will create. If this value is not in the range [start, len(list)-1], your function should return an empty list.
If the
parameter values are acceptable, your function will return a list that
contains the items from source beginning at the index start and ending
at the index end (inclusive). This is different from the Python slice
operator, as the item at the index end is also included in the new
list.
This is what I got so far:
list1 = []
def slice(list1):
list1 = list(input("enter a list"))
emptylist = []
st = int(input("enter start"))
ed = int(input("enter end"))
if ed not in range(st,len(list1)-1) or st not in range(0,len(list1)-1):
print(emptylist)
else:
list2 = []
for i in list1:
list2.append(list1[i])
return(list2)
print(slice(list1))
I don't know how the list input is supposed to be delimited, so I'm not going to include it in my answer. In fact, I'm just going to assume that we have somehow received a list of proper format from the user and only need to call the function on that list.
Your function would take 3 parameters, start, end, and the list itself because these are bare minimum requirement for a slicing task.
def slice(lst, start, end):
The simplest solution would be to iterate through the loop and add all the elements within the start:end range. After all, you are allowed to use indexing notation.
You would first have to create an empty list that will contain only the appropriate elements. We'll call this list an output list.
output = []
Then, we can iterate through all the integers between start and end because list indices are integers.
for i in range(start, end):
output.append(lst[i])
At the end of the function, you would want to return the output list so you end up with an actually sliced list object.
return output
Putting it all together:
# some shebangs here
'''
Some docstring here
'''
# some imports here
# receive user input. You need a list and two integers.
# lst = input()
# start = input()
# end = input()
def slice(lst, start, end):
output = []
if not(0 <= start < len(lst)):
return output
elif not(start <= end < len(lst)):
return output
else:
for i in range(start, end+1):
output.append(lst[i])
return output
print(slice(lst, start, end))
Why doesn't your original script work?
Issue with your code is in the last line. If you do:
for i in list1:
list2.append(list1[i])
return(list2)
First of all, i is the element being iterated, not the index of the element. Second, returning terminates the function, and therefore the loop. As a result, not only will the script throw you an IndexError, but even if you're lucky, it will also return a list with only one element. You can modify it like this:
for i in list1:
list2.append(i)
return list2
This would work, except that now we're ignoring the start and end parameters. (This is why I opted for the range() function). If you want to use the Pythonic notation, you would enumerate through the list, check the index, and append if appropriate.
for index, element in enumerate(list1):
if start <= index <= end:
list2.append(element)
return list2

Logical Bug in the Code Related to Slicing in Python

Consider the following piece of code that generates all subsets of size k of an array [1,2,3,...,n]:
def combinations(n, k):
result = []
directed_combinations(n, k, 1, [], result)
return result
def directed_combinations(n, k, offset, partial_combination, result):
if len(partial_combination) == k:
new_partial = [x for x in partial_combination]
result.append(new_partial)
return
num_remaining = k - len(partial_combination)
i = offset
# kind of checks if expected num remaining is no greater than actual num remaining
while i <= n and num_remaining <= n - i + 1:
partial_combination.append(i)
directed_combinations(n, k, i + 1, partial_combination, result)
del partial_combination[-1]
# partial_combination = partial_combination[:-1] <-- same funcationality as line above, but produces weird bug.
i += 1
print(combinations(n=4,k=2))
For example, combinations(n=4,k=2) will generate all subsets of length 2 of [1,2,3,4].
There are two lines in the code that produce a list with the last element removed. I tried accomplishing it with del and creating a brand new list by slicing off the last element (i.e. [-1]). The version with del produces the correct result. But, version with [-1] doesn't. There is no runtime error; just a logical bug (i.e. incorrect result).
I suspect this has something to do with creating a new list when doing slicing vs. keeping the same list with del. I can't seem to understand why this is an issue.
I didn't notice at first that your function is recursive (should've read your tags better).
You're right, functionally the two are almost the same. Here is the exact same thing:
# del partial_combination[-1] # working (mutate)
# partial_combination = partial_combination[:-1] # different (rebind)
partial_combination[:] = partial_combination[:-1] # same (mutate)
The result of each of the above will be that you end up with a list containing the same elements. But while del and partial_combination[:] mutate your original list, the middle one rebinds the name to a new list with the same elements. When you pass on this new list to the next recursive step, it will operate on its own copy rather than on the single list the previous recursive levels are working on.
To prove this, you can call print(id(partial_combination)) after each of the above options, and see that the id changes in the rebinding case, while it stays the same throughout the mutating ones.

Modifying a list in place: Calculating a sieve of primes in a python list

Problem number 10 of Project Euler asks us to calculate the sum of all primes below 2,000,000.
My algorithm for the same is:
Construct a sieve for numbers under 2,000,000
Sum all the numbers in the sieve.
I am not comfortable with the code creating multiple lists instead of performing computations over the same list.
Given below is my code:
def main(number_above):
list_of_numbers = list(range(number_above))
list_of_numbers = calculate_sieve(list_of_numbers)
print summation_of_primes(list_of_numbers)
def calculate_sieve(list_of_numbers):
for prime in list_of_numbers:
if prime >= 2 and prime != 'C':
multiple = 2
while multiple * prime < len(list_of_numbers):
list_of_numbers[ prime * multiple ] = 'C'
multiple += 1
return list_of_numbers
def summation_of_primes(list_of_numbers):
summation = 0
for element in list_of_numbers:
if element != 'C':
summation += element
return summation - 1
The steps in which the lists are created:
First, a list is created of numbers in range(2,000,000)
Second, this list is passed on to the calculate_sieve function which cancels all composites.
Then, the calculate_sieve function returns a list to the main function.
Finally, this list is passed on to the summation function.
Is python operating on the same list in place, or does it hold more than one list at a time?
If it is creating multiple copies of the list, is there a way to minimize memory usage by operating on the list in place?
Is python operating on the same list in place?
Yes, mostly.
passing a list as an argument to a function does not create a new list.
modifying elements in a list does not create a new list.
returning a list from a function does not create a new list.
The only code you have that may potentially create a duplicate list is this line:
list_of_numbers = list(range(number_above))
In Python 2.7 and below, range already returns a list. Calling list on the result will create a second list. You can safely just write list_of_numbers = range(number_above) and save yourself a little memory.
In 3.X, range returns a range object, so the list call is necessary there, if you want to subsequently do assignment, like list_of_numbers[ prime * multiple ] = 'C'.
You can check it very easily:
In [1]: def some_func(seq):
...: seq.append(1)
...:
In [2]: s = []
In [3]: some_func(s)
In [4]: s
Out[4]: [1]
Since s got modified some_func cannot operate on a copy of it.
Identifiers (or variables) in python are always references to an object. They are not memory locations. They are names to an object. When you pass an object to a function the arguments simply creates a new reference to that object, so there's no copying involved.
I believe all your computations are performed on the same list in place, a list is mutable and it is passed by reference, hence I believe in your case there will only be one list in memory at the same time.

Insert on python list on a particular offset

I'm having a python list preinitialized like this:
eventarray=[None]*10000
Is there any other way to preinitialize this list,so that 10000 can be a variable to be changed from different functions inside,without changing the data in the list.
Note:preinitialisation is done for inserting value on a particular offset of list.
It is hard to tell exactly what you are asking, so this might not be what you want. If it is not, you should clarify your question.
To create a list of n elements where every element is None, you can do this:
myList = [None]*n
n must be in scope to do this, and future changes to n will not effect the list length.
If you want to insert a value into a list, and shift the rest of the values right, you can use list.insert(i,x):
myList = [None]*10000
myList.insert(5,0)
This will insert a 0 at index 5, and will shift the remaining values (indices 5 and higher) to the right one. This results in a list who's length is one more than before the insert. If you just want to set a value in your list:
myList = [None]*10000
myList[5] = 0
This will result in a list of 10,000 elements with the 6th element (index of 5) being 0. The length of the initial and final lists will be the same.
From what I can infer from your question, you want to create a list with global scope, with its length determined at runtime.
Edit: The following code now extends the list as necessary each time my_list_creator is accessed:
foo = []
def my_list_creator(n):
'''Makes the nth value in a list n. If the list is not
long enough, it extends it, initialised with None.
'''
global foo
if len(foo) < n:
foo.extend([None] * (n - len(foo)))
foo[n-1] = n
def access_a_global():
print foo
def another_func():
my_list_creator(10)
access_a_global()
my_list_creator(20)
access_a_global()
another_func()
foo is global in scope, but is initialised as necessary.

Categories

Resources