Functional Programming

From www.norsemathology.org

Jump to: navigation, search

Functional Programming is "is a programming paradigm that treats computation as the evaluation of mathematical functions" Wikipedia. While Mathematica is primarily a functional programming environment, Python/Sage is hybrid of procedural programming and object-oriented programming. Nonetheless it is still possible to code in a functional programming style in Python.

Contents

Functions Operating on Lists

Both Sage and Mathematica provide a means of applying a function to each item in a list.

Let's say we want to find the derivative of each item in a list.

Sage:

var('x')
expressions = [x*cur for cur in srange(1,5,include_endpoint=True)]
map( diff, expressions )

The map function takes a function f as it's first parameter, and iterable objects as parameters. The number of iterable objects (e.g. lists, tuples) passed depends on the number of parameters you wish to pass to f. If f only takes on parameter then only one iterable object can be passed.

What map does is create a new list where the items of the new list are the result of the function f applied to each object in the iterable object passed in as a parameter.

Mathematica:

xPowers = Times[Range[5],x]
D[xPowers,x]

Issues

If you look at our example of finding the derivative of all elements in a list, the Sage version has a very serious issue. Let's say the expressions contained the variables x and y and you wanted the derivative with respect to y instead of x. This isn't possible the way we wrote the code above in Sage, however it is possible with the Mathematica code. In Mathematica you would just change the line D[xPowers,x] to D[xPowers,y].

All is not lost however Python's support for lambda functions makes this a pretty easy fix.

Example:

var('x y')
expressions = [3*x*y + 4*y + 5*x, 4*x*y^2 + 5*y + 4*x]
f = lambda expr : expr.diff(y)
map(f, expressions)

What we did here is create a simple lambda function that returns the derivative of expr with respect to y. Then we call map with f instead of diff.

Creating a List Plot

Sage:

g = lambda val : val^2
pointList = map( g, srange(-3,3,.1) )
list_plot( pointList )

Output:

Image: Sage_list_plot.png

Mathematica:

pointList = Table[x^2, {x, -3, 3, .1}]
ListPlot[pointList]

Output:

Image: Mathematica_list_plot.jpg

More on Map

Both Sage and Mathematica provide a map function for applying a function to a list of values.

Simple Example

Let's say we have a vector and want to square each element and add one to it.

Sage

v = [2,4,5]
f(x) = x^2 + 1
map(f,v)

Mathematica

v = { 2,4,5 }
f[x_] := x^2 + 1
Map[f,v]

More Complex Example

Let's say that we want to generate a list of lists of rational numbers and then take that list a generate a list of each rational number paired with it's square.

Mathematica

ratios=Table\left[\frac{i}{j},\{i,10\},\{j,10\}\right]

Output:

{{1,1/2,1/3,1/4,1/5,1/6,1/7,1/8,1/9,1/10},{2,1,2/3,1/2,2/5,1/3,2/7,1/4,2/9,1/5},{3,3/2,1,3/4,3/5,1/2,3/7,3/8,1/3,3/10},
{4,2,4/3,1,4/5,2/3,4/7,1/2,4/9,2/5},{5,5/2,5/3,5/4,1,5/6,5/7,5/8,5/9,1/2},{6,3,2,3/2,6/5,1,6/7,3/4,2/3,3/5},
{7,7/2,7/3,7/4,7/5,7/6,1,7/8,7/9,7/10},{8,4,8/3,2,8/5,4/3,8/7,1,8/9,4/5},{9,9/2,3,9/4,9/5,3/2,9/7,9/8,1,9/10},
{10,5,10/3,5/2,2,5/3,10/7,5/4,10/9,1}}

squarePair[x\_]:=\left\{x,x^2\right\}

pairs=Map[squarePair, ratios, \left\{2\right\}]

Output:

{{{1,1},{1/2,1/4},{1/3,1/9},{1/4,1/16},{1/5,1/25},{1/6,1/36},{1/7,1/49},{1/8,1/64},{1/9,1/81},{1/10,1/100}},{{2,4},{1,1},
{2/3,4/9},{1/2,1/4},{2/5,4/25},{1/3,1/9},{2/7,4/49},{1/4,1/16},{2/9,4/81},{1/5,1/25}},{{3,9},{3/2,9/4},{1,1},{3/4,9/16},
{3/5,9/25},{1/2,1/4},{3/7,9/49},{3/8,9/64},{1/3,1/9},{3/10,9/100}},{{4,16},{2,4},{4/3,16/9},{1,1},{4/5,16/25},{2/3,4/9},
{4/7,16/49},{1/2,1/4},{4/9,16/81},{2/5,4/25}},{{5,25},{5/2,25/4},{5/3,25/9},{5/4,25/16},{1,1},{5/6,25/36},{5/7,25/49},
{5/8,25/64},{5/9,25/81},{1/2,1/4}},{{6,36},{3,9},{2,4},{3/2,9/4},{6/5,36/25},{1,1},{6/7,36/49},{3/4,9/16},{2/3,4/9},{3/5,9/25}},
{{7,49},{7/2,49/4},{7/3,49/9},{7/4,49/16},{7/5,49/25},{7/6,49/36},{1,1},{7/8,49/64},{7/9,49/81},{7/10,49/100}},{{8,64},{4,16},
{8/3,64/9},{2,4},{8/5,64/25},{4/3,16/9},{8/7,64/49},{1,1},{8/9,64/81},{4/5,16/25}},{{9,81},{9/2,81/4},{3,9},{9/4,81/16},
{9/5,81/25},{3/2,9/4},{9/7,81/49},{9/8,81/64},{1,1},{9/10,81/100}},{{10,100},{5,25},{10/3,100/9},{5/2,25/4},{2,4},{5/3,25/9},
{10/7,100/49},{5/4,25/16},{10/9,100/81},{1,1}}}

Note that in the Map function the {2} passed as the last parameter specifies which part of ratios to apply the function squarePair to. The 2 in this case is telling it to apply it to the items in the inner lists.

Sage

ratios = [ [ i/j for j in srange(1,10,include_endpoint=True) ] for i in srange(1,10,include_endpoint=True) ]

Output:

[[1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10], [2, 1, 2/3, 1/2,
2/5, 1/3, 2/7, 1/4, 2/9, 1/5], [3, 3/2, 1, 3/4, 3/5, 1/2, 3/7, 3/8, 1/3,
3/10], [4, 2, 4/3, 1, 4/5, 2/3, 4/7, 1/2, 4/9, 2/5], [5, 5/2, 5/3, 5/4,
1, 5/6, 5/7, 5/8, 5/9, 1/2], [6, 3, 2, 3/2, 6/5, 1, 6/7, 3/4, 2/3, 3/5],
[7, 7/2, 7/3, 7/4, 7/5, 7/6, 1, 7/8, 7/9, 7/10], [8, 4, 8/3, 2, 8/5,
4/3, 8/7, 1, 8/9, 4/5], [9, 9/2, 3, 9/4, 9/5, 3/2, 9/7, 9/8, 1, 9/10],
[10, 5, 10/3, 5/2, 2, 5/3, 10/7, 5/4, 10/9, 1]]
squarePair = lambda x : [x,x^2]

Notice here that we can't just enter map(squarePair,ratios). This is because ratios is a list of lists and map acts on the values of a given list, which with our input would also be lists. So to actually run map on the elements of the inner lists we have to iterate through ratio and run map on each iteration.

pairs = [map(squarePair, _ ) for _ in ratios]

Output:

[[[1, 1], [1/2, 1/4], [1/3, 1/9], [1/4, 1/16], [1/5, 1/25], [1/6, 1/36],
[1/7, 1/49], [1/8, 1/64], [1/9, 1/81], [1/10, 1/100]], [[2, 4], [1, 1],
[2/3, 4/9], [1/2, 1/4], [2/5, 4/25], [1/3, 1/9], [2/7, 4/49], [1/4,
1/16], [2/9, 4/81], [1/5, 1/25]], [[3, 9], [3/2, 9/4], [1, 1], [3/4,
9/16], [3/5, 9/25], [1/2, 1/4], [3/7, 9/49], [3/8, 9/64], [1/3, 1/9],
[3/10, 9/100]], [[4, 16], [2, 4], [4/3, 16/9], [1, 1], [4/5, 16/25],
[2/3, 4/9], [4/7, 16/49], [1/2, 1/4], [4/9, 16/81], [2/5, 4/25]], [[5,
25], [5/2, 25/4], [5/3, 25/9], [5/4, 25/16], [1, 1], [5/6, 25/36], [5/7,
25/49], [5/8, 25/64], [5/9, 25/81], [1/2, 1/4]], [[6, 36], [3, 9], [2,
4], [3/2, 9/4], [6/5, 36/25], [1, 1], [6/7, 36/49], [3/4, 9/16], [2/3,
4/9], [3/5, 9/25]], [[7, 49], [7/2, 49/4], [7/3, 49/9], [7/4, 49/16],
[7/5, 49/25], [7/6, 49/36], [1, 1], [7/8, 49/64], [7/9, 49/81], [7/10,
49/100]], [[8, 64], [4, 16], [8/3, 64/9], [2, 4], [8/5, 64/25], [4/3,
16/9], [8/7, 64/49], [1, 1], [8/9, 64/81], [4/5, 16/25]], [[9, 81],
[9/2, 81/4], [3, 9], [9/4, 81/16], [9/5, 81/25], [3/2, 9/4], [9/7,
81/49], [9/8, 81/64], [1, 1], [9/10, 81/100]], [[10, 100], [5, 25],
[10/3, 100/9], [5/2, 25/4], [2, 4], [5/3, 25/9], [10/7, 100/49], [5/4,
25/16], [10/9, 100/81], [1, 1]]]

Solving Quadratic Equations

This example illustrates how to create a function to solve a quadratic equation.

Sage

Code:

def realSolve( eqn , var ):
    polynomial = eqn.subtract_from_both_sides( eqn.lhs() ).rhs()
    c,b,a = [ _[0] for _ in polynomial.coefficients(var) ]
    discriminant = b^2 - 4*a*c
    if discriminant < 0:
        return []
    elif discriminant > 0:
        return [ factor( ( -b - sqrt(discriminant) ) / (2*a) ) , factor( ( -b - sqrt(discriminant) ) / (2*a) ) ]
    else:
        return [ -b / (2*a) ]

Let's start with the first line:

polynomial = eqn.subtract_from_both_sides( eqn.lhs() ).rhs()

What this does is take a symbolic equation and make into symbolic arithmetic. This is done by subtracting the left hand side of the equation from both sides and then putting the right hand side into the variable polynomial.

Here we get the coefficients a, b, and c:

c,b,a = [ _[0] for _ in polynomial.coefficients(var) ]

Variables of the type SymbolicArithmetic have a method called coefficients that returns a list of the coefficients for a given variable, and the power of that coefficient, ordered by in ascending order by power.

So if we had variable = 3*x^2 + 10*x + 5 then variable.coefficients(x) would return [ [5,0],[10,1],[3,2] ]. Since for our calculations we only need the coefficients for a,b,c we strip them out.

So with our example variable = 3*x^2 + 10*x + 5, c,b,a = [ _[0] for _ in variable.coefficients(x) ] would set a=3,b=10,c=5.

Mathematica

Code:

realSolve[eqn_,var_]:=
Module[{polynomial,a,b,c,discriminant},
polynomial=First[eqn]-Last[eqn];
{c,b,a}=CoefficientList[polynomial,var];
discriminant=b^2-4a c;
If[discriminant <0,{},
If[discriminant>0,{(-b-Sqrt[discriminant])/(2a),(-b+Sqrt[discriminant])/(2a)},
{-b/(2a)}
] ] ]
polynomial=First[eqn]-Last[eqn];

Here we can use the functions First and Last to get the left and right hand sides of the equation, and subtract the right hand side from the left hand side, and assign the result to the variable polynomial.

{c,b,a}=CoefficientList[polynomial,var];

The CoefficientList function takes a expression and returns the coefficients for the variable specified in the second parameter as a list, sorted in ascending order by power.

Personal tools