Where is this f coming from in python. Factor calculator - python

I couldn't figure out how my professor got this f in the function. The program works.
factors.py
from isfactor import is_factor
def factors(n):
factor_list = [1]
for f in range(2, n):
if is_factor(n ,f):
factor_list.append(f)
factor_list.append(n)
return factor_list
Second file: isfactor.py
def is_factor(n, possible_factor):
if n% possible_factor == 0:
return True
else:
return False
Third file: testfactors.py
from factors import factors
print(factors(12))
print(factors(13))
print(factors(1000))

Let's say you have a list. A simple list
var mylist = [10, 11, 12]
Cool. Now, I want to print each number on that list. Now, you can access a list item in python with brackets [#], where # is an index number (starts with 0, so first item is 0, second is 1, etc.). so I can do this:
print mylist[0] #or print(mylist[0]) for python 3.x
print mylist[1]
print mylist[2]
But that would be tedious. So we can, instead, iterate. go one by one with a for loop, which looks like this
for item in mylist
print item
This means that now it goes through every single item on the list, says item = mylist[0] and then you can do something. then it loops again, now item = mylist[1] etc. It's a temporary variable, and it makes things much simpler, right?
Now range is a built in function in python that creates a list of numbers from x to y, i.e. range(1, 10) will result in the list [1, 2, 3, 4, 5, 6, 7, 8, 9]*
So when you see for f in range(2, n) you know that now starts a loop, where each time the loop passes, f will be equal to 2, then 3, then 4... up until n, which is a variable the function factors recieves.
Makes more sense now?
*p.s. it's not exactly a list. In Python 3.x it became it's own type, I'm not getting into it, but you can read more here

Consider the following loop:
Input:
for f in range(2, 10):
print(f)
Output:
2
3
4
5
6
7
8
9
When you call for f in range(2, n-1):, you are actually creating a variable f and setting it to 2, running the loop, then setting f to 3, running the loop, etc. until you get to n (then the iteration stops).

Related

List index out of range error while performing a binary search

I attempted to create a function that takes an ordered list of numbers and a given number, and decides whether or not the given number is inside the list. I am trying to use a binary search to accomplish this task.
I have two steps:
First, I am making list1 smaller by only taking the numbers in list1 that are smaller than the given number, and then appending those numbers into a new list, called newlist.
Next, in the while loop, I am basically taking all the numbers that are less than the number in the middle of the newlist and removing them, repeating that process multiple times until there is only one number in newlist. From there, I would compare that number to the given number. My code is shown below.
list1 = [1, 3, 5, 6, 8, 14, 17, 29, 31]
number = 7
def func(list1, number):
newlist = []
for x in list1:
if x < number:
newlist.append(x)
else:
continue
while len(newlist) > 1:
for x in range(0, len(newlist) - 1):
if newlist[x] < newlist[round(len(newlist) / 2)]:
newlist.remove(newlist[x])
else:
continue
if newlist[0] == number:
return True
else:
return False
print(func(list1, number))
I am receiving an error at line 36 (if newlist[x] < newlist[round(len(newlist) / 2)]:), that the list index is out of range. I think that the problem is that as newlist is getting smaller and smaller, the x value set by range(0, len(newlist) - 1) is staying the same?? If that is the case, I am unsure of how to remedy that. Much thanks in advance.
The issue is this bit right here:
for x in range(0, len(newlist) - 1):
if newlist[x] < newlist[round(len(newlist) / 2)]:
newlist.remove(newlist[x])
First, you're iterating over the list
[0, 1, 2, ..., len(newlist) - 1]
This list is generated when you start the loop, meaning that if len(newlist) is 7 at the beginning, the list will always go up to 6, regardless of whether things are removed from newlist, which you later do. This is what causes your error, since at some point you've removed enough elements that your list is now, say, three elements large, but python is trying to access the fifth element because the list it's iterating over isn't newlist, it's [0, 1, 2, 3, 4, 5, 6].
To fix this, you could (for example) replace the for loop with this:
x = 0
while x < len(newlist - 1):
if newlist[x] < newlist[round(len(newlist) / 2)]:
newlist.pop(x) # simple way of saying "remove item at index x"
This is essentially the way of doing a C or Java-style for loop in python, and will avoid this type of problem.
I also understand that you have an issue with the logic in your code, which was pointed out in one of the comments above and gets more to the heart of your underlying issue, but this is at least an explanation of why this error occurred in the first place, so maybe it's helpful to you in the future

Python Magic number guesser

I am trying to remove numbers from a list until there is only one item left. I am having trouble with my for loop. It keeps giving the error stating:
File "C:/Users/kramsey/Desktop/Python/NumberGuesser2.py", line 28, in <module>
list.remove(B)
ValueError: list.remove(x): x not in list
Code:
B = random.choice(list)
if ready == "ok":
lessthan = input("is your number less than {}".format(B)).strip().lower()
if lessthan == "yes":
for numbers in list:
B = int(B+1)
list.remove(B)
print(list)
I want it to delete the numbers that are not less than the random.choice(list) without printing an error to the user once it hits an integer that isn't in the list.
It always tells the user that list.remove(x) not in list and doesn't continue through my for loop.
Let us simplify your problem to having a list (appropriately named l):
l = [1, 3, 4, 6, 7, 9]
and a random number (for simplicity's sake, we we'll just set this to 5):
B = 5
Now, we can't iterate over l and remove items from l as this will lead us into problems (as you have seen with that error). So the best way to do this is re-define l as a new list that is made with a list-comprehension.
l = [i for i in l if i > B]
and that will leave l as:
[6, 7, 9]
This is the right way to do what you want and hopefully you can incorporate this into your code.
Assuming your list is consisted of sorted numbers, you would have to do something like that:
if lessthan == 'yes':
index = len(list) - 1
while list[index] >= B:
list.remove(list[index])
index -= 1
print(list)
Bear in mind, that you if you are going to alter the list you are iterating over by removing/deleting elements, you should iter backwards(from the end of list to the beginning). Otherwise, you could skip over some elements by deleting an element in the middle of the list.

Add a 0 after each even number in list [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am trying to add a 0 after each even number, every time i run the code it infinitely prints the value of i, which is 0 every time.
I've tried this so far:
def linear_list (n):
x = []
for y in range (1, n+1):
x.append (y)
return x
n = int (input ("Enter the lenght of the list: "))
alist = linear_list (n)
for i in alist:
print (i)
if i % 2 == 0:
alist.append(0)
print (alist)
But for i >= 2my code is printing infinite zeroes:
Enter the lenght of the list: 5
0
0
0
0
...
Expected output:
[1, 2, 0, 3, 4, 0, 5, 6, 0, 7, 8, 0, 9, 10, 0]
How can achieve the correct list?
Make it work, make it right, make it fast
Here the make it work part.
You are modifying the list and iterating it at the same time.
Try:
otherlist = []
for i in alist:
print (i)
otherlist.append(i)
if i % 2 == 0:
otherlist.append(0)
print (otherlist)
You're increasing the alist length every time you append to it, and since you're looping through it you will never exit the loop - the list never ends. You don't want to change the list, but the value within it, so enumerate it:
for i, v in enumerate(alist):
print (v)
if v % 2 == 0:
alist[i] = v * 10 # assuming it's a number, use v + "0" if its a string
The iterator create by the for loop is a separate object from the list you iterate over. Your for loop is similar to a while loop like
itr = iter(a)
while True:
try:
i = next(itr)
except StopIteration:
break
if i % 2 == 0:
a.append(0)
a.append always adds a 0 to the end of the list, without affecting the current position of the iterator. Since 0 is even, once the iterator reaches the what was the end of the list when you started the loop, it sees a 0, and so another 0 gets added to the end of the list. It continues reading the next 0 and adding another 0, on and on, forever.
Technically, you can do what you want, but it's rather tricky and very easy to get wrong. (It took me about 8 tries to get this example right.) The trick is to create a separate iterator explicitly, one that you can access in the body of the loop, rather than letting the for loop generate the iterator for you. This allows you to insert a 0 after the current value in the list, then skip over the new 0 so that you won't see it on the next iteration of the loop.
Don't actually do this if you can avoid it: it is far simpler to create a new list that replaces the old list after the loop.
That said, on with the show:
a = range(10)
itr = enumerate(a) # A list of index, value pairs
for index, value in itr:
if value % 2 == 0:
i = index + 1
a[i:i] = [0] # Put a 0 after the current position
next(itr) # Skip over the 0 you just added
assert a == [0, 0, 1, 2, 0, 3, 4, 0, 5, 6, 0, 7, 8, 0, 9]
You can shorten this a bit by starting the index at 1 instead of 0, effectively pre-adding 1 to each index before you need it.
a = range(10)
itr = enumerate(a, start=1)
for i, value in itr:
if value % 2 == 0:
a[i:i] = [0]
next(itr)

Traversing a Python list and making in-place changes

My task is to remove all instances of one particular element ('6' in this example) and move those to the end of the list. The requirement is to traverse a list making in-line changes (creating no supplemental lists).
Input example: [6,4,6,2,3,6,9,6,1,6,5]
Output example: [4,2,3,9,1,5,6,6,6,6,6]
So far, I have been able to do this only by making supplemental lists (breaking the task's requirements), so this working code is not allowed:
def shift_sixes(nums):
b = []
c = 0
d = []
for i in nums:
if i == 6:
b.insert(len(nums),i)
elif i != 6:
c = c +1
d.insert(c,i)
ans = d + b
return ans
I've also tried list.remove() and list.insert() but have gotten into trouble with the indexing (which moves when I insert() then move the element to the end): For example -
a = [6,4,6,2,3,6,9,6,1,6,5]
def shift_sixes(nums):
for i in nums:
if i == 6:
nums.remove(i)
nums.insert(nums[len(nums)-1], 0)
elif i != 0:
i
shift_sixes(a)
Additionally, I have tried to use the enumerate() function as follows, but run into problems on the right hand side of the b[idx] assigment line:
for idx, b in enumerate(a):
a[idx] = ???
Have read other stackoverflow entries here, here and here, but they do not tackle the movment of the element to one end.
Would appreciate any help on this list traversal / inplace switching issue. Many thanks.
EDIT
#eph - thank you. this is indeed an elegant response. I am sure it will pass my 'no new list' requirement? I surely intend to learn more about lambda and its uses
#falsetru - thank you for the reminder of the append/pop combination (which I tried to do in my original query via list.remove() and list.insert()
#tdelaney - thank you as well. somehow your response is closest to what I was attempting, but it seems not to pass the test for [0, 0, 5].
It is a bad idea to modify list while traverse. You can either make a copy to traverse, or generate a new list during traverse.
In fact, the question can be done in many ways, such as:
>>> a.sort(key = lambda i: i == 6)
>>> a
[4, 2, 3, 9, 1, 5, 6, 6, 6, 6, 6]
Iterating the list reverse way, pop the element if it's 6, then append it.
xs = [6,4,6,2,3,6,9,6,1,6,5]
for i in range(len(xs)-1, -1, -1): # 10 to 0
if xs[i] == 6:
xs.append(xs.pop(i))
Why not try something like this?
Basically, the approach is to first count the number of values.
If 0, then returns (since Python produces a ValueError if the list.index method is called for an element not in the list).
We can then set the first acceptable index for the value to be the length of the list minus the number of occurrences it exists in the list.
We can then combine list.pop/list.append to then traverse the list until all the values desired occur at the end of the list.
def shift_value(lst, value):
counts = lst.count(value) # 5
if not counts:
return lst
value_index = len(lst) - counts
index = lst.index(value)
while index != value_index:
lst.append(lst.pop(index))
index = lst.index(value)
return lst
lst = [6,4,6,2,3,6,9,6,1,6,5]
print(shift_value(lst, 6))
EDIT: This is horribly inefficient, better answer suggested above.
This requires O(n^2) time, rather than O(n) time.
The key term here is "In Line". The way you do that is move num[i] = num[i+1] for each i to the end of the list.
def shift_sixes(num):
for i, val in enumerate(num):
if val == 6:
# shift remaining items down
for j in range(i,len(num)-1):
num[j] = num[j+1]
# add 6 at the end
num[-1] = 6
return num
print(shift_sixes([1,9,4,6,2,7,8,6,2,2,6]))
print(shift_sixes([1,2,3]))
print(shift_sixes([6]))
print(shift_sixes([3]))
Use two runners. First from front to end checking for 6s, second from end to front pointing to last item that's not a 6. Keep swapping (a[i+1], a[i] = a[i], a[i+1]) until they meet.
Catch: this is not stable like in a stable sort. But I don't see that as a requirement.
Will try to write working code when in front of a python interpreter with a keyboard.
In case you need a stable sort (i.e. order of elements that are not 6 should remain the same), then the solution is:
def move_to_end(data, value):
current = 0 # Instead of iterating with for, we iterate with index
processed = 0 # How many elements we already found and moved to end of list
length = len(data) # How many elements we must process
while current + processed < length: # While there's still data to process
if data[current] == value: # If current element matches condition
data.append(data.pop(current)) # We remove it from list and append to end
processed += 1 # Our index remains the same since list shifted, but we increase number of processed elements
else: # If current element is not a match
current += 1 # We increase our index and proceed to next element
if __name__ == '__main__':
print
print 'Some testing:'
print
for test_case in (
[1, 9, 4, 6, 2, 7, 8, 6, 2, 2, 6], # Generic case
[6, 6, 6, 6], # All items are 6
[1, 7], # No items are 6
[], # No items at all
):
print 'Raw:', test_case
move_to_end(test_case, 6)
print 'Becomes:', test_case
print
Note that this solution retains the order of not only non-matching elements, but of matching elements as well. So for example, if you change the check condition from "equal to 6" to "is an even number", all elements matching the condition will be moved to the end of list while retaining their order among themselves.
Why not keep it simple?
a = [6,4,6,2,3,6,9,6,1,6,5]
def shift_sixes(nums):
for i in range(0,len(nums)):
if nums[i] == 6:
nums.append(nums.pop(i))
>>> shift_sixes(a)
>>> a
[3, 9, 1, 5, 2, 4, 6, 6, 6, 6]

Modify list with loop in Python: Is my method of doing so wrong?

I have just started learning Python so i just wanted to get something straight.
I want to make a function that repeatedly appends the sum of the current last three elements of the list "lst" to "lst", x number of "times".
As far as i understand you should not change a list while iterating over it, so my second solution is wrong, right? Even though it produces the same result as the first function?
def appendsums(lst, times):
count = 0
for i in range(times):
if count <= times:
sums = sum(lst[-3:])
lst.append(sums)
count += 1
return lst
Here is my second solution
def appendsums(lst, times):
count = 0
while count <= times:
sums = sum(lst[-3:])
lst.append(sums)
count += 1
return lst
Regards
You're correct, you shouldn't iterate over a list while editing. But as pointed by other users, none of the above examples are iterating over the list lst.
Here is an example of iteration:
for item in lst:
# Do something
If you need to iterate over a list while editing it, make a copy and iterate over the copy:
copy_lst = lst[:]
for item in copy_lst:
# edit lst
I'd stick with the following code:
def appendsums(lst, times):
for i in range(times):
lst.append(sum(lst[-3:]))
return lst
Its generally unsafe because iterators on the container aren't informed that a change has occurred. You usually want to create a temporary list and then modify the target list at the end.
Also, as a side note, I think you may want count to be less than times (and not equal to it).
In its current form, when I set times to 5, it adds six entries.
>>> def appendsums(lst, times):
... count = 0
... while count <= times:
... sums = sum(lst[-3:])
... lst.append(sums)
... count += 1
... return lst
...
>>> appendsums([1,2,3], 5)
[1, 2, 3, 6, 11, 20, 37, 68, 125]

Categories

Resources