I am trying to save the results of 10 function calls into a list. however, when I want to access this numerical data, I keep getting type errors, because the list is really a list of function calls. How do I make it do stuff so I can have numbers to do compare?
I tried setting temp variable to result of each spot in the list, but it still shows as a function.
Output should show the average for different # of dice, and return the best amount of dice to roll
def calculator(fn, num_samples=1000): # this is the function I'm trying to call
def print_and_return(*args):
total3 = 0
for _ in range(num_samples):
total3 += (fn(*args))
return float(total3)/num_samples
return print_and_return
def find_average(dice=six_sided):
avglist = []
k, spot = 0, 0
bob = roll_dice(k+1, dice)
passed_function = calculator(bob, 1000)
print(passed_function)
while k <= 10:
avglist.append((passed_function)) # <==trying to save the results when I check with 1-10 dice rolls
if k == 0:
spot = 1
else:
temp = 0
max = 0
i = 0
while i <= len(avglist):
temp = avglist[i]
if max > temp:
max = temp
i +=1
if (avglist[k] > temp):
spot = k
print(k, "dice scores", avglist[k], "on average")
k +=1
return spot
You are not calling passed_function. Calling a function with no arguments still reqires the ().
Your calculator function returns a function, not a plain value. You therefore need to call passed_function in order to get your result.
Based on the arguments to your call to roll_dice, it looks like you want the k argument to vary in the loop. As written now, it will not. I suspect you are tripping yourself up a bit. The code could be written a whole lot simpler without so many function references being passed around.
Related
So far I have written this much
def find_num_min(xlst):
x = []
count = 0
w = 0
for i in range(len(xlst)):
if len(xlst) == 0:
return ()
else:
y = sorted(xlst)
w = y[0]
return w
You could do it like that, but python has many built in functions that can help you write shorter, more readable code:
def find(xlst):
smallest = min(xlst)
count = xlst.count(smallest)
print(f"The smallest number is {smallest}, it occurs {count} times.")
return smallest, count
In old boring inefficient longhand, just to show you what this could look like,
def find_num_min(xlst):
smallest = None
count = 0
for elt in xlst:
if not count or elt < smallest:
smallest = elt
count = 1
elif elt == smallest:
count += 1
return smallest, count
The condition if not count takes care to set smallest to something in the first iteration (and prevents us from attempting to compare elt to smallest when the latter is None, which woud cause Python to error out); in every subsequent iteration, count will be at least 1. (Go over this with pen and paper on a few simple example lists to make sure this is true.)
But definitely learn to look for existing code before writing your own. Both of the subtasks here are easily - and in many ways, better - done with existing functions from Python's standard library.
use numpy
import numpy as np
x = np.array(xlst)
minimum = x.min()
count = (x == minimum).sum()
using Counter
from collections import Counter
lst = [2,1,2,3,2,1,1,2,3,4,2,2]
c = Counter(lst)
lowest = min(c.keys())
print(f"lowest key: {lowest}, how many times: {c[lowest]}")
Using lambda function:
find_num_min = lambda xlst: (None, 0) if len(xlst) == 0 else (
(smallest := min(xlst)),
xlst.count(smallest)
)
Here is how a lambda function works, showing the same function in lambda form and normal function form:
# normal function
def func(param1, param2):
return param1 + param2
# lambda function
func = lambda (param1, param2): param1 + param2
Next, there are shortened if statements called ternary operators. These two statements are the same, as an example:
# normal if statements
if num > 0:
print("positive")
else:
print("negative")
# ternary
print("positive" if num > 0 else "negative")
The := operator is the same as the = operator, but it returns the value in addition to setting the value of whatever is on the left. Example:
print(n := 5) # prints 5 while setting n to 5
print(n) # also prints 5, as n was set to 5
This is useful because it means we don't have to find the minimum value twice.
Finally, the min function is built into Python and can be used to find the minimum value in an list. You can also use the count method on a list to find the number of times a value occurs in the list.
Putting all of these pieces together, we arrive at the final, optimized, one-line function at the beginning of this answer.
This is for a school assignment.
I have been tasked to define a function determining the largest square pyramidal number up to a given integer(argument). For some background, these are square pyramidal numbers:
1 = 1^2
5 = 1^2+2^2
14 = 1^2+2^2+3^2
So for a function and parameter largest_square_pyramidal_num(15), the function should return 14, because that's the largest number within the domain of the argument.
I get the idea. And here's my code:
def largest_square_pyramidal_num(n):
sum = 0
i = 0
while sum < n:
sum += i**2
i += 1
return sum
Logically to me, it seemed nice and rosy until I realised it doesn't stop when it's supposed to. When n = 15, sum = 14, sum < n, so the code adds one more round of i**2, and n is exceeded. I've been cracking my head over how to stop the iteration before the condition sum < n turns false, including an attempt at break and continue:
def largest_square_pyramidal_num(n):
sum = 0
for i in range(n+1):
sum += i**2
if sum >= n:
break
else:
continue
return sum
Only to realise it doesn't make any difference.
Can someone give me any advice? Where is my logical lapse? Greatly appreciated!
You can do the following:
def largest_pyr(x):
pyr=[sum([i**2 for i in range(1,k+1)]) for k in range(int(x**0.5)+1)]
pyr=[i for i in pyr if i<=x]
return pyr[-1]
>>>largest_pyr(15)
14
>>> largest_pyr(150)
140
>>> largest_pyr(1500)
1496
>>> largest_pyr(15000)
14910
>>> largest_pyr(150000)
149226
Let me start by saying that continue in the second code piece is redundant. This instruction is used for scenario when you don't want the code in for loop to continue but rather to start a new iteration (in your case there are not more instructions in the loop body).
For example, let's print every number from 1 to 100, but skip those ending with 0:
for i in range(1, 100 + 1):
if i % 10 != 0:
print(i)
for i in range(1, 100 + 1):
if i % 10 == 0:
# i don't want to continue executing the body of for loop,
# get me to the next iteration
continue
print(i)
The first example is to accept all "good" numbers while the second is rather to exclude the "bad" numbers. IMHO, continue is a good way to get rid of some "unnecessary" elements in the container rather than writing an if (your code inside if becomes extra-indented, which worsens readability for bigger functions).
As for your first piece, let's think about it for a while. You while loop terminates when the piramid number is greater or equal than n. And that is not what you really want (yes, you may end up with a piramid number which is equal to n, but it is not always the case).
What I like to suggest is to generate a pyramid number until in exceedes n and then take a step back by removing an extra term:
def largest_square_pyramidal_num(n):
result = 0
i = 0
while result <= n:
i += 1
result += i**2
result -= i ** 2
return result
2 things to note:
don't use sum as a name for the variable (it might confuse people with built-in sum() function)
I swapped increment and result updating in the loop body (such that i is up-to-date when the while loop terminates)
So the function reads like this: keep adding terms until we take too much and go 1 step back.
Hope that makes some sense.
Cheers :)
Alright, I wrote the following code to find the union of any number of probabilities (wonderful article on the topic here: https://www.thoughtco.com/probability-union-of-three-sets-more-3126263):
#Finds all Intersections
def intersection_finder1(poss, intersection_number):
#Make Lists of descending possibilities intersection_number times, Works for 2, not 3
sub_posses = []
use = poss
sub_posses.append(use)
for i in range(intersection_number - 1):
#print use[(i+1):], intersection_number
sub_posses.append(use[(i+1):])
return sub_posses
def sub_poss_modifier(sub_posses):
for x in range(len(sub_posses)):
del sub_posses[x][0]
return sub_posses
def intersection_finder2(sub_posses, intersection_number, ongoing_sum=0):
#Iterate over lists for list_item_number_1
#Increment lists by one
#Repeat for last_list length times,
#Commented out below are debugging things
multi = 1
for y in range(len(sub_posses[-1])):
for i in range(len(sub_posses)):
multi = multi * sub_posses[i][y]
#print multi, sub_posses[i][y], intersection_number
#print "-----------RESTART------------"
ongoing_sum += multi
multi = 1
if len(sub_posses[-1]) > 1:
new_lt = sub_poss_modifier(sub_posses)
return intersection_finder2(new_lt, intersection_number, ongoing_sum)
return ongoing_sum
def combiner(poss):
#Sums Possbilities
total = sum(poss)
ongoing_total = total
#Adds/Subtracts Combinations
for i in range(2, len(poss) + 1):
#Somehow, poss is changing. I have no idea how.
#Say poss is [1/2.0, 1/2.0, 1/2.0]. If you put that in, the function works. If you put poss in, it doesn't
print poss
sub_poss = intersection_finder1(poss, i)
aors = intersection_finder2(sub_poss, i)
#print aors, i
if i % 2 == 0:
ongoing_total -= aors
else:
ongoing_total += aors
#Returns total Possbility
return ongoing_total
print combiner([1/2.0, 1/2.0, 1/2.0])
It works but only if I make a specific change in which the value of the variable poss is inserted in place of itself. For Example (the only change is in the 9th line of combiner):
#Finds all Intersections
def intersection_finder1(poss, intersection_number):
#Make Lists of descending possibilities intersection_number times, Works for 2, not 3
sub_posses = []
use = poss
sub_posses.append(use)
for i in range(intersection_number - 1):
#print use[(i+1):], intersection_number
sub_posses.append(use[(i+1):])
return sub_posses
def sub_poss_modifier(sub_posses):
for x in range(len(sub_posses)):
del sub_posses[x][0]
return sub_posses
def intersection_finder2(sub_posses, intersection_number, ongoing_sum=0):
#Iterate over lists for list_item_number_1
#Increment lists by one
#Repeat for last_list length times,
#Commented out below are debugging things
multi = 1
for y in range(len(sub_posses[-1])):
for i in range(len(sub_posses)):
multi = multi * sub_posses[i][y]
#print multi, sub_posses[i][y], intersection_number
#print "-----------RESTART------------"
ongoing_sum += multi
multi = 1
if len(sub_posses[-1]) > 1:
new_lt = sub_poss_modifier(sub_posses)
return intersection_finder2(new_lt, intersection_number, ongoing_sum)
return ongoing_sum
def combiner(poss):
#Sums Possbilities
total = sum(poss)
ongoing_total = total
#Adds/Subtracts Combinations
for i in range(2, len(poss) + 1):
#Somehow, poss is changing. I have no idea how.
#Say poss is [1/2.0, 1/2.0, 1/2.0]. If you put that in, the function works. If you put poss in, it doesn't
#print poss
sub_poss = intersection_finder1([1/2.0, 1/2.0, 1/2.0], i)
aors = intersection_finder2(sub_poss, i)
#print aors, i
if i % 2 == 0:
ongoing_total -= aors
else:
ongoing_total += aors
#Returns total Possbility
return ongoing_total
print combiner([1/2.0, 1/2.0, 1/2.0])
By doing a little debugging, I found that the variable poss changes throughout each iteration of the for loop -- thus yielding an incorrect answer. Furthermore, it only changes in code block #1; in code block #2, poss stays the same. So far, I haven't been able to find any instance where I redefine or alter poss in any function. Also, even if I did alter poss somewhere, the only difference between code block #1 and #2 is the list in the ninth line of the function combiner. Yet, block #2 yields the correct answer, while block #1 doesn't.
The Terminal Output from block #1 (printing poss):
[0.5, 0.5, 0.5]
[0.5, 0.5]
0.75
The Terminal Output from block #2 (printing poss):
[0.5, 0.5, 0.5]
[0.5, 0.5, 0.5]
0.875
So far, to prevent poss from changing, while maintaining some level of general use, I've tried to redefine it and rename it. What can I do to stop poss from changing, while making it calculate different probabilities as simple as changing a variable?
By the way, I fairly new to programming, so any advice to make my code better or myself a better programmer as a whole would be greatly appreciated.
"So far, I haven't been able to find any instance where I redefine or
alter poss in any function."
You alter the objects referred to by your function parameter posse quite clearly. You essentially pass the result of intersection_finder1 to intersection_finder2 (apt names...) In intersection_finder1, you append posse to sub_posses. In other words, in the partitions of the set, you use the object itself to represent the improper subset.
def intersection_finder1(poss, intersection_number):
#Make Lists of descending possibilities intersection_number times, Works for 2, not 3
sub_posses = []
use = poss # NOT MAKING A COPY
This says the object referred to by posse is now also referred to by use
sub_posses.append(use) # now sub_posses includes posse
for i in range(intersection_number - 1):
#print use[(i+1):], intersection_number
sub_posses.append(use[(i+1):])
return sub_posses
Now, in intersection_finder2 you call sub_poss_modifier on sub_posses:
def sub_poss_modifier(sub_posses):
for x in range(len(sub_posses)):
del sub_posses[x][0]
return sub_posses
Where you clearly modify sub_posses[0], which will be posse
The solution? Make copies. You an use the built-in list method posse.copy(), or the well-understood python copy-idiom, poss[:].
def intersection_finder1(poss, intersection_number):
#Make Lists of descending possibilities intersection_number times, Works for 2, not 3
sub_posses = []
use = poss.copy()
And everywhere else where you don't want to share objects, but want a list of the same values...
Note
To get a better grip of how Python variables and values work, I suggest reading all of Ned Batchelder's Facts and myths about Python names and values.
Also you should know that these operations produce shallow copies. If you have more complicated data-structures than a list of immutable objects, say, a list of lists, or a list of dicts, you may need a deep-copy instead. You should know the difference.
When you are passing a list, it is passed as an object, not as a copy, and any assignment will refer to the object as reference.
You are appending poss to sub_posses in intersection_finder1(). This appends poss through its reference, not a copy, even if assigned to use.
In sub_poss_modifier() you delete some elements of sub_posses. This actually deletes the elements of poss too.
Solution is to make a copy when appending to sub_posses in intersection_finder1():
sub_posses.append(use[:])
I want to find multiples of 3 or 5 below 1000 using the code below:
a=[]
b=[]
def multiples_of_3():
i=1
for i in range(330):
m=i*3
if(m<1000):
a.append(m)
def multiples_of_5():
j=1
for j in range(330):
k=j*5
if(k<1000):
b.append(k)
if __name__ == "__main__":
multiples_of_3()
multiples_of_5()
print sum(a) + sum(b)
Result- 262355
Result is not right. It should be 233168 . How am I going wrong with the logic here?
You are looping over the wrong ranges and adding multiples of 15 twice.
I believe that this is the smallest change to your program that makes it work:
a=[]
b=[]
def multiples_of_3():
i=1
for i in range(334): # stops at 1 less than the value passed to `range`
m=i*3
if(m<1000):
a.append(m)
def multiples_of_5():
j=1
for j in range(330): # could change to 201 and still work
k=j*5
if(k<1000):
b.append(k)
if __name__ == "__main__":
multiples_of_3()
multiples_of_5()
print sum(set(a+b))
But you should probably rethink your approach. Using global variables is generally a bad idea - looking at the call multiples_of_3(), there is no way to know what the subroutine is doing with those multiples; the variable a is not referenced anywhere, yet before the line and after the line it has two different values. So for starters, I would turn the subroutines into pure functions - have them return the arrays instead of modifying globals.
As minor stylistic points, you also don't need to use different variable names inside the two functions, since they're all local. You don't need to assign anything to i before the loops (the loop will create the variable for you), and you don't need parentheses around the condition in Python's if statement (the colon takes care of delimiting it):
def multiples_of_3():
a = []
for i in range(334):
m = i * 3
if m < 1000:
a.append(m)
return a
def multiples_of_5():
a = []
for i in range(201):
m = i * 5
if m < 1000:
a.append(m)
return a
if __name__ == "__main__":
a = multiples_of_3()
b = multiples_of_5()
print sum(set(a+b))
You could also combine the two multiples_of functions into a single generic one that takes a parameter telling it what to return multiples of:
def multiples_of(k):
result = []
for i in range(1000/k+1):
multiple = i * k
if multiple < 1000:
result.append(multiple)
return result
You could even turn the maximum value into an optional parameter:
def multiples_of(k, under=1000):
result = []
for i in range(under/k+1):
multiple = i * k
if multiple < under:
result.append(multiple)
return result
Either way, your main part becomes this:
a = multiples_of(3)
b = multiples_of(5)
print sum(set(a+b))
Finally, just as a point of reference, it is possible to do the whole thing as a one-liner. Here I've switched from building up the list of multiples by actually doing the multiplication, to just looping over all the numbers under 1000 and testing them for divisibility by either 3 or 5:
print sum([n for n in range(1000) if n%3==0 or n%5==0])
Shouldn't for j in range(330): be for j in range(200): since you're using multiples of 5?
I am supposed to write a function for one of my computer science classes in python. The function is supposed to take in a startValue and then increment it until numberOfValues is reached. This is my function so far:
def nextNValues(startValue, increment, numberOfValues):
result = int(0)
for i in range(0, numberOfValues):
increase = i * increment
result = startValue + increase
return result
I call it by doing:
print(nextNValues(5,4,3))
The problem is that the output is only 13. How do I make it so it returns a number each time it increments. For example, 5, 9, 13? I have been having this problem with my previous functions but I have just been adding and removing things without much logic to get it to work. What am I doing wrong?
This is a perfect use case for generators.
Long story short, just use yield instead of return:
def nextNValues(startValue, increment, numberOfValues):
result = int(0)
for i in range(0, numberOfValues):
increase = i * increment
result = startValue + increase
yield result
The clients of your code can then use it either in a simple loop:
for value in nextNValues(...):
print(value)
Or they can get a list if needed by converting it with list.
For example, if one needed to print the result:
print(list(nextNValues(...)))
You should build up a list. Each time through the iteration, append the number to the list. Then, at the end, simply return the list.
def nextNValues(startValue, increment, numberOfValues):
result = []
for i in range(0, numberOfValues):
increase = i * increment
#result = startValue + increase
result.append(startValue + increase)
return result