[Tutor] nested functions
Kirby Urner
urnerk@qwest.net
Tue, 09 Apr 2002 19:26:57 -0700
At 05:11 PM 4/9/2002 -0500, Cameron Stoner wrote:
>Why would you want to define functions inside functions?
>It seems kinda like a class with methods. When would you
>want to do something like this?
Indeed, as Danny pointed out, you might use a function
in a function to create a custom function.
Here's another math example -- also links to matrices,
an earlier topic.
To rotate point around the x-axis in the XYZ coordinate
system, we use the rotation matrix:
def xmatrix(theta):
return [[1 ,0 , 0],
[0, cos(theta),-sin(theta)],
[0, sin(theta), cos(theta)]]
where theta is the angle you want to rotate by. Notice
we use a function with theta as an argument. Other,
similar matrices are used to rotate around the Y and
Z axes respectively:
def ymatrix(theta):
return [[cos(theta), 0,-sin(theta)],
[0 , 1 , 0],
[sin(theta), 0, cos(theta)]]
def zmatrix(theta):
return [[ cos(theta),-sin(theta),0],
[ sin(theta), cos(theta),0],
[ 0 ,0 , 1]]
Notice the matrix data structure: rows are lists
within a list. Each row has the same number of
entries. These happen to be 3x3 square matrices.
Now suppose we frequently need to rotate points a fixed
amount, say 45 degrees around Z. We can manufacture a
function that does this.
The function called mkrot() below takes one of the
above matrix functions, and the angle of rotation, as
its inputs:
def mkrot(matrix,theta):
"""
Makes functions that rotate a point around a
fixed axis by theta degrees
"""
theta = theta*(pi/180)
R = matrix(theta) # R is a specific rotation matrix
def rotate(v):
"Matrix multiplication of R by v (a vector)"
newcoords = []
for row in R:
newcoords.append(
reduce(add,map(mul,v.xyz,row)))
return Vector(newcoords) # vector points in new direction
return rotate
Now let's use this function. we pass zmatrix as the first
argument. Recall that zmatrix is a function, which expects
and angle as its argument. So we also pass the angle (45.)
which gets converted from degrees to radians in the first
line of mkrot().
zrot45 = mkrot(zmatrix,45.)
So theta gets passed to the matrix function (zmatrix in
this case -- might have been xmatrix or ymatrix) to create
rotation matrix R, and R features inside the internal
rotate() function. It's this internal rotate() function
which mkrot() returns, i.e. it's returning a *function*
which rotates a point 45 degrees around the Z axis.
So zrot45 is now a function. It expects a Vector object
as input (defined elsewhere). Let's use it:
>>> zrot45
<function rotate at 0x00B003F0>
>>> v = Vector([1,0,0]) # Vector an imported class
>>> zrot45(v)
Vector (0.70710678118654757, 0.70710678118654746, 0.0)
The arrow has moved. (1,0,0) points to 3'oclock, but
after zrot45 does its job, the arrow has moved counter
clockwise by 45 degrees (the Z axis sticks through the
clock face, so rotations around it are in the plane of
the clock).
The guts of zrot45 is a simple matrix multiplication of
R (fixed by mkrot) times the single argument vector.
In a module I added to recently, I use this system to
compose two rotation functions, i.e. to make a single
rotation out of components:
theta = acos(-1/3.)*180/pi/2
xrot54 = mkrot(xmatrix,theta)
zrot45 = mkrot(zmatrix,45.)
def rotxz(v):
return xrot54(zrot45(v))
rotxz will return a function that rotates any input vector
45 degrees around the Z axis, and then about 54 degrees
around the X axis. I can now use rotxz(v) anywhere I
like in my program, knowing what it's been pre-programmed
to do. And I can use mkrot() to manufacture other fixed
rotation functions, simply by handing it a matrix and an
angle.
Kirby