What does it mean to pass a function to another function? - python

I have the following code:
def print_function(values, test):
for x in values:
print('f(', x,')=', test(x), sep='')
def poly(x):
return 2 * x**2 - 4 * x + 2
print_function([x for x in range(-2, 3)], poly)
I understand what the poly function does. What I don't understand is the test(x)? What does this do and where is the connection to the poly function?

In python, functions can be stored in other variables and then called as functions themselves.
Example
def myfunction():
print('hello')
myfunction() #prints hello
alias_myfunction = myfunction # store function object in other variable
alias_myfunction() #prints hello as well
# use id to test if myfunction and alias_myfunction point to same object.
print(id(myfunction) == id(alias_myfunction))
Output
hello
hello
True
Both myfunction and alias_myfunction store the reference to the function object
This can be confirmed using id
In this particular case
# values -> [x for x in range(-2, 3)]
# test -> poly
def print_function(values, test):
for x in values:
print('f(', x,')=', test(x), sep='')
# When test(x) is called, it is essentially calling poly(x)
def poly(x):
return 2 * x**2 - 4 * x + 2
print_function([x for x in range(-2, 3)], poly)

Related

How to pass a function as an argument

I would like to find an approximate value for the number pi = 3.14.. by using the Newton method. In order to use it also for some other purpose and thus other function than sin(x), the aim is to implement a generic function that will be passed over as an argument. I have an issue in passing a function as an argument into an other function. I also tried lambda in different variations. The code I am showing below produces the error message: IndexError: list index out of range. I will appreciate your help in solving this issue and eventually make any suggestion in the code which may not be correct. Thanks.
from sympy import *
import numpy as np
import math
x = Symbol('x')
# find the derivative of f
def deriv(f,x):
h = 1e-5
return (lambda x: (f(x+h)-f(x))/h)
def newton(x0,f,err):
A = [x0]
n = 1
while abs(A[n]-A[n-1])<=err:
if n == 1:
y = lambda x0: (math.f(x0))
b = x0-y(x0)/(deriv(y,x0))
A.append(b)
n += 1
else:
k = len(A)
xk = A[k]
y = lambda xk: (math.f(xk))
b = newton(A[k],y,err)-y(newton(A[k],y,err))/deriv(y,k)
A.append(b)
n += 1
return A, A[-1]
print(newton(3,math.sin(3),0.000001))
I don't know why you use sympy because I made it without Symbol
At the beginning you have to calculate second value and append it to list A and later you can calculate abs(A[n]-A[n-1]) (or the same without n: abs(A[-1] - A[-2])) because it needs two values from this list.
Other problem is that it has to check > instead of <=.
If you want to send function sin(x) then you have to use math.sin without () and arguments.
If you want to send function sin(3*x) then you would have to use lambda x: math.sin(3*x)
import math
def deriv(f, x, h=1e-5):
return (f(x+h) - f(x)) / h
def newton(x0, f, err):
A = [x0]
x = A[-1] # get last value
b = x - (f(x) / deriv(f, x)) # calculate new value
A.append(b) # add to list
while abs(A[-1] - A[-2]) > err: # it has to be `>` instead of `<=`
x = A[-1] # get last value
b = x - (f(x) / deriv(f, x)) # calculate new value
A.append(b) # add to list
return A, A[-1]
# sin(x)
print(newton(3, math.sin, 0.000001)) # it needs function's name without `()`
# sin(3*x)
print(newton(3, lambda x:math.sin(3*x), 0.000001))
# sin(3*x) # the same without `lambda`
def function(x):
return math.sin(3*x)
print(newton(3, function, 0.000001))
Result:
([3, 3.1425464414785056, 3.1415926532960112, 3.141592653589793], 3.141592653589793)
([3, 3.150770863559604, 3.1415903295877707, 3.1415926535897936, 3.141592653589793], 3.141592653589793)
EDIT:
You may write loop in newton in different way and it will need <=
def newton(x0, f, err):
A = [x0]
while True:
x = A[-1] # get last value
b = x - (f(x) / deriv(f, x)) # calculate new value
A.append(b) # add to list
if abs(A[-1] - A[-2]) <= err:
break
return A, A[-1]

Loop a function using the previous output as input

When my function foo generating a new element, I want to reuse the output and put it in foo n-times. How can I do it?
My function:
def foo(x):
return x + 3
print(foo(1))
>>>4
For now. I'm using this method:
print(foo(foo(foo(1))))
There are a couple ways to do what you want. First is recursion, but this involves changing foo() a bit, like so:
def foo(x, depth):
if depth <= 0:
return x
return foo(x+3, depth-1)
and you'd call it like foo(1, n)
The other way is with a loop and temp variable, like so
val = 1
for _ in range(0, n):
val = foo(val)
Use a loop for this:
value = 1
for i in range(10):
value = foo(value)
def foo(x,y):
for i in range(y):
x = x + 3
return x
print (foo(10,3))
Output:
19
What you are searching for is called recursion:
def foo(x, n=1):
if n == 0:
return x
return foo(x + 3, n - 1)
Another possible with lambda and reduce
Reduce function
from functools import reduce
def foo(x):
return x + 3
print(reduce(lambda y, _: foo(y), range(3), 1))
You will get 10 as result
# y = assigned return value of foo.
# _ = is the list of numbers from range(3) for reduce to work
# 3 = n times
# 1 = param for x in foo

How to compose a function n times in python

I know how to compose two functions by taking two functions as input and output its composition function but how can I return a composition function f(f(...f(x)))? Thanks
def compose2(f, g):
return lambda x: f(g(x))
def f1(x):
return x * 2
def f2(x):
return x + 1
f1_and_f2 = compose2(f1, f2)
f1_and_f2(1)
You use a loop, inside a nested function:
def compose(f, n):
def fn(x):
for _ in range(n):
x = f(x)
return x
return fn
fn will be have closure that retains references to the f and n that you called compose with.
Note this is mostly just copied from https://stackoverflow.com/a/16739439/2750819 but I wanted to make it clear how you can apply it for any one function n times.
def compose (*functions):
def inner(arg):
for f in reversed(functions):
arg = f(arg)
return arg
return inner
n = 10
def square (x):
return x ** 2
square_n = [square] * n
composed = compose(*square_n)
composed(2)
Output
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
if you want to make it one line composition,
then try this,
def f1(x):
return x * 2
def f2(x):
return x + 1
>>> out = lambda x, f=f1, f2=f2: f1(f2(x)) # a directly passing your input(1) with 2 function as an input (f1, f2) with default so you dont need to pass it as an arguments
>>> out(1)
4
>>>
>>> def compose(f1, n):
... def func(x):
... while n:
... x = f1(x)
... n = n-1
... return x
... return func
>>> d = compose(f1, 2)
>>> d(2)
8
>>> d(1)
4
>>>
you can use functools.reduce:
from functools import reduce
def compose(f1, f2):
return f2(f1)
reduce(compose, [1, f2, f1]) # f1(f2(1))
output:
4
if you want to compose same function n times:
n = 4
reduce(compose, [1, *[f1]*n]) # f1(f1(f1(f1(1))))
output:
16

Create a new_function which has an input n and input_function which return a output_function that does what input_function does n times

I would like to create a new function that has two inputs (n, input_function) which returns a new output_function that does what input_function does but it does it n times. Here is the image of what i'm trying to accomplish
def repeat_function(n, function, input_number):
for i in range(n):
input_number = function(input_number)
return input_number
def times_three(x):
return x * 3
print(repeat_function(3, times_three, 10)) #prints 270 so it's correct
print(times_three(times_three(times_three(10)))) #prints 270 so it's correct
#This function does not work
def new_repeat_function(n, function):
result = lambda x : function(x)
for i in range(n-1):
result = lambda x : function(result(x))
return result
new_function = new_repeat_function(3, times_three)
#I want new_function = lambda x : times_three(times_three(times_three(x)))
print(new_function(10)) # should return 270 but does not work
I tried my best to implement it but it does not work. I need new_repeat_function to do what repeat_function does but instead of returning and integer answer like repeat_function does, new_repeat_function has to return time_three() n times.
You need to return a function, not the result.
def new_repeat_function(n, function):
def repeat_fn(x):
result = function(x)
for i in range(n - 1):
result = function(result)
return result
return repeat_fn
Here, you construct a closure that captures the parameters n and function. Later, when you call the returned closure, it uses n and function as they were passed to new_repeat_function. So, if we call
new_func = new_repeat_function(3, three_times)
print(new_func(10))
we get 270 as expected.
What you need to do is to create a function that builds another function, in python this pattern is called decorators, here you have an example:
from functools import wraps
def repeated(n):
def wrapped(f):
#wraps(f)
def ret_f(*args, **kwargs):
res = f(*args, **kwargs)
for _ in range(1, n):
res = f(res)
return res
return ret_f
return wrapped
#repeated(2)
def times_three(x):
return x * 3
print(times_three(10))
You can try it live here
You can simply recur on repeat itself. Ie, you don't need to create a range or use a loop at all
def repeat (n, f):
return lambda x: \
x if n is 0 else repeat (n - 1, f) (f (x))
def times_three (x):
return x * 3
new_func = repeat (3, times_three)
print (new_func (10))
# 270
You can skip intermediate assignments as well
print (repeat (0, times_three) (10))
# 10
print (repeat (1, times_three) (10))
# 30
print (repeat (2, times_three) (10))
# 90

how to set .__name__ in python?

I am trying to solve this exercise:
Return a function that represents the polynomial with these
coefficients.
For example, if coefs=(10, 20, 30), return the function of x that computes
30 * x**2 + 20 * x + 10. Also store the coefs on the .coefs attribute of
the function, and the str of the formula on the .__name__ attribute.
This is my solution:
def poly(coefs):
#write the string name
l=len(coefs)
coefs=reversed(coefs)
j=0
name=""
for i in coefs:
if j<l-2:
name=name+str(i)+" * x**"+str(l-j-1)+" + "
elif j==l-2:
name=name+str(i)+" * x + "
else:
name=name+str(i)
j=j+1
def calc(x):
name.replace("x",str(x))
calc.__name__=name
return eval(name)
return calc
It does not work very well.
>>> p=poly((1,2,3))
>>> p
<function calc at 0x3b99938> #the name of p is not what I want!!! (*)
>>> y=p(3)
>>> p
<function 3 * x**2 + 2 * x + 1 at 0x3b99938> # now this is right!
>>>
How can I have the right name also in the first call (*) ?
Set the name outside of the function object:
def calc(x):
newname = name.replace("x", str(x))
calc.__name__ = newname
return eval(name)
calc.__name__ = name
return calc
Note that str.replace() does not replace values in strings in-place. It returns the altered string, string values themselves are immutable.
Your initial name will have to use x, since the value of x is not known at the time you call poly(). I'd leave out filling in of x in the name however; the function will not return that exact calculation the next time you call it with a different x. Without replacing x in the name each time you call the function, calc() would simply be:
def calc(x):
return eval(name)
Together with adding name = '' at the top of your poly() function, with the namechange per call in place still, gives:
>>> p = poly((1,2,3))
>>> p
<function 3 * x**2 + 2 * x + 1 at 0x10ecf5488>
>>> p(3)
34
>>> p
<function 3 * 3**2 + 2 * 3 + 1 at 0x10ecf5488>

Categories

Resources