Here are some examples of using functions, passing arguments, and so forth. I have mentioned several times that functions are just data values that are stored on in memory and referenced by namespace entries. This means that functions can be passed as arguments to functions. Here are several examples: keep in mind that a function is an unnamed block of code which is stored as a data value.
Here is code to compute the Riemann Sum in python. It takes a function as its first parameter, the start and end x coordinates, and an optional number of iterations.
def riemann(f, xstart, xend, iterations=50):
# Compute the area under the curve f from xstart to xend
delta = (float(xend) - float(xstart)) / iterations
area = 0.0
for i in range(iterations):
# compute the x coordinate
x = xstart + i * delta
boxarea = delta * f(x) # Note the call to the function f
area += boxarea
return area
We can use this by passing in a function for the first argument.
import math
print(riemann(math.cos, 0, 4))
print(riemann(math.cos, 0, 4, 100))
print(riemann(math.sin, xend=15, xstart=0))
print(riemann(math.sin, iterations=100, xstart=0, xend=2*math.pi)) # Why does this not print zero?
Note that math.cos
is a namespace entry for the (unnamed) function located somewhere in memory. During the call to riemann
, a namespace entry for f
is created which references this location in memory.
We can also call riemann
with our own functions.
def squareplustwo(x):
return x * x + 2
print(riemann(xstart=4, xend=15, f=squareplustwo, iterations=100))
Because creating functions to be passed to other functions is quite common, python provides a way to create functions as an expression and without giving them a name: the lambda (see also here). This creates a function value out in memory and then immediately sets up the namespace entry for f
to point to it.
print(riemann(lambda x: x*x+2, 4, 15, 100))
print(riemann(lambda x: x ** 4 - 3*x, 20, 200))
One of the most common ways of storing colors in the computer is the RGB Color Model. To store images, we organize an image to a rectangular grid of pixels. The color of each pixel is stored via the RGB color model. There are no image manipulation facilities in the python standard library. Instead, there are a number of 3rd party libraries that work with images, perhaps the most common is pillow. Instead of using this library, we will create images directly using tkinter.
This library expects colors in web form, meaning a string like this: #2E8B57 which happens to be a sea green. To understand this, we split it into three chunks of two digits: 2E 8B 57. These are hexadecimal numbers, in this case the numbers 46, 139, and 87. The 46 is the red component, the 139 is the green component, and the 87 is the blue component. These numbers are out of 255 and therefore we view the color as \(46/255\) or \(18%\) red, \(139/255\) or \(50%\) green, and \(87/255\) or \(34%\) blue.
One nice technique to create lists in python is list comprehension. The tutorial has some good examples so I won't repeat it here.
The goal is to create Julia Fractals. The Wikipedia page is somewhat complicated, but what we are going to do is the following:
Think of the image as a region of the plane. We will use by default -2 <= x <= 2
and -1.25 <= y <= 1.25
but allow the user to specify the exact region of the plane to view.
Next, we divide this region into tiny boxes, each box will become one pixel. Again, we will take parameters for how many pixels should be in the width and height and give them reasonable defaults.
For each box/pixel, we create a single complex number from the coordinates of the box. The x coordinate becomes the real component, and the y coordinate becomes the complex component.
Repeatedly apply a function f
to this complex number and count how long it takes for the value to go above a cutoff in absolute value. The number of steps it takes will determine the color of the pixel. We bound the maximum number of iterations before aborting.
Display the final image.
Here is the code
import tkinter
import cmath
def julia(f, max_iter=100, cutoff=2.0, image_width=700, image_height=500, x_min=-2.0, x_max=2.0, y_min=-1.25, y_max=1.25):
# Given an r, g, b (numbers between 0 and 255), this function creates a web label
# color string like #2E8B57.
def make_color(r, g, b):
return "#%02x%02x%02x" % (r%255, g%255, b%255)
# Use a list comprehension to create a list of colors. The result will be a
# list of strings. The colors I am using here is a collection of colors spanning
# gradually from teal to aqua since that is a mix of green and blue with zero red.
colors = [ make_color(0, i*2+40, i*2+40) for i in range(max_iter) ]
# This function is given a complex point, and computes the color. It does
# this by repeatedly applying the function f (note the namespace lookups
# needed to find f). Once the value goes above the cutoff, the iteration
# count is used as an index into the colors list to determine the color.
def color_for(c):
for trials in range(max_iter):
if abs(c) <= cutoff:
c = f(c)
else:
break
return colors[trials]
# Given a pixel coordinate, this function returns a complex number for the point of
# the plane representing this pixel.
def coord_for_pixel(pixel_x, pixel_y):
percentX = pixel_x / float(image_width)
percentY = pixel_y / float(image_height)
x = x_min + percentX * (x_max - x_min)
y = y_max - percentY * (y_max - y_min)
return complex(x, y)
# Use a list comprehension to create a list of (pixel_x, pixel_y, color) entries
# The color is found by passing the return value from coord_for_pixel to color_for.
# The list comprehension then makes px and py range through the entire grid.
pixels = [ (px, py, color_for(coord_for_pixel(px, py))) for px in range(image_width) for py in range(image_height) ]
return pixels
# This function uses tkinter to draw the image, you can ignore this.
def draw(pixels):
image_width = max([ p[0] for p in pixels])
image_height = max([ p[1] for p in pixels])
root = tkinter.Tk()
im = tkinter.PhotoImage(width=image_width, height=image_height)
for p in pixels: im.put(p[2], (p[0], p[1]))
l = tkinter.Label(root, image=im)
l.pack()
root.mainloop()
# Some images
pixels = julia(lambda x: complex(1,0.3)*cmath.sin(x), cutoff=50, x_min=-2, x_max=2)
#pixels = julia(lambda x: x * x + complex(-0.70176, -0.3842))
#pixels = julia(lambda x: x * x + complex(-0.70176, -0.3842), x_min=-0.1, x_max=0.1, y_min=-0.1, y_max=0.1)
#pixels = julia(lambda x: x * x + complex(0.285, 0.01))
#pixels = julia(lambda x: complex(1,0.4)*cmath.sin(x), cutoff=50, x_min=-2, x_max=2)
draw(pixels)
The Mandelbrot set is one of the most famous fractals. Update the code above to draw the Mandelbrot set. To do so, most of the above code can be left alone. The changes you need are
julia
function to mandelbrot
and remove the parameter f
color_for
function to compute the color for a point. For the Mandelbrot fractal, you do not change the value c
which is passed in. Instead, you initialize a variable z
to zero and then iterate the Mandelbrot formula z = z*z + c
. You still must loop at most max_iterations
times, breaking out if the value of z
goes above the cutoff. For Mandelbrot, a cutoff of two is enough. The number of iterations when the loop is broken out of is the color index, exactly like julia.mandelbrot
function and pass the resulting pixels to draw