An idealized mathematical problem can usually only be approximated using a finite number of steps in finite precision. A complete set of instructions for transforming data into a result is called an algorithm. In most cases it is reasonable to represent an algorithm by another mathematical function, denoted here by .
Even simple problems can be associated with multiple algorithms.
1.3.1Algorithms as code¶
Descriptions of algorithms may be presented as a mixture of mathematics, words, and computer-style instructions called pseudocode, which varies in syntax and level of formality. In this book we use pseudocode to explain the outline of an algorithm, but the specifics are usually presented as working code.
Of all the desirable traits of code, we emphasize clarity the most after correctness. We do not represent our programs as always being the shortest, fastest, or most elegant. Our primary goal is to illustrate and complement the mathematical underpinnings, while occasionally pointing out key implementation details.
As our first example, Function 1.3.1 implements an algorithm that applies Horner’s algorithm to a general polynomial, using the identity
The quoted lines at the beginning of Function 1.3.1 are a documentation string. The function itself starts off with the keyword function
, followed by a list of its input arguments. The first of these is presumed to be a vector, whose length can be obtained and whose individual components are accessed through square bracket notation. After the computation is finished, the return
keyword indicates which value or values are to be returned to the caller.
The Polynomials
package for Julia provides its own fast methods for polynomial evaluation that supersede our simple Function 1.3.1 function. This will be the case for all the codes in this book because the problems we study are well-known and important. In a more practical setting, you would take implementations of basic methods for granted and build on top of them.
1.3.2Writing your own functions¶
Any collection of statements organized around solving a type of problem should probably be wrapped in a function. One clue is that if you find yourself copying and pasting code, perhaps with small changes in each instance, you should probably be writing a function instead.
Functions can be defined in text files with the extension .jl
, at the command line (called the REPL prompt), or in notebooks.
As seen in Function 1.3.1, one way to start a function definition is with the function
keyword, followed by the function name and the input arguments in parentheses. For example, to represent the mathematical function , we could use
function myfun(x)
s = sin(x)
return exp(s)
end
The return
statement is used to end execution of the function and return one or more (comma-separated) values to the caller of the function. If an executing function reaches its end
statement without encountering a return
statement, then it returns the result of the most recent statement, but this is considered poor style.
For a function with a short definition like the one above, there is a more compact syntax to do the same thing:
myfun(x) = exp(sin(x))
You can also define anonymous functions or lambda functions, which are typically simple functions that are provided as inputs to other functions. This is done with an arrow notation. For example, to plot the function above (in the Plots
package) without permanently creating it, you could enter
plot(x -> exp(sin(x)), 0, 6)
As in most languages, input arguments and variables defined within a function have scope limited to the function itself. However, they can access values defined within an enclosing scope. For instance:
mycfun(x) = exp(c*sin(x))
c = 1; mycfun(3) # returns exp(1*sin(3))
c = 2; mycfun(3) # returns exp(2*sin(3))
There’s a lot more to be said about functions in Julia, but this is enough to get started.
Functions can be defined in text files with the extension .m
, at the command line, or in Live Scripts.
As seen in Function 1.3.1, one way to start a function definition is with the function
keyword, followed one or more output arguments in brackets, the function name, and the input arguments in parentheses. For example, to represent the mathematical function , we could use the following in a file called myfun.m
:
function [y] = myfun(x)
s = sin(x);
y = exp(s);
end
Whatever value is assigned to y
when the function terminates will be returned as the output of the function.
For a function with a short definition like the one above, there is a more compact syntax to do the same thing:
myfun = @(x) exp(sin(x));
The syntax on the right of the =
above defines an anonymous function (called a lambda function in computer science), which can be used in place without giving it a name as we did here. We’ll have examples of doing this later on.
As in most languages, input arguments and variables defined within a function have scope limited to the function itself. However, they can access values defined within an enclosing scope, with those values being locked in at the time of creation. For instance:
c = 1;
mycfun = @(x) exp(c*sin(x));
mycfun(3) % returns exp(1*sin(3))
c = 2;
mycfun(3) % also returns exp(1*sin(3))
mycfun = @(x) exp(c*sin(x)); % redefines mycfun
mycfun(3) % now returns exp(2*sin(3))
There’s a lot more to be said about functions, but this is enough to get started.
Functions can be defined in text files with the extension .py
, at the command line (called the REPL prompt), or in notebooks.
As seen in Function 1.3.1, one way to start a function definition is with the def
keyword, followed by the function name and the input arguments in parentheses, ending with a colon. The statements for the body of the function must then all be indented. For example, to represent the mathematical function , we could use
def myfun(x):
s = np.sin(x)
return np.exp(s)
The return
statement is used to end execution of the function and return one or more (comma-separated) values to the caller of the function.
For a function with a short definition like the one above, there is a more compact syntax to do the same thing:
myfun = lambda x : np.exp(np.sin(x))
The syntax on the right of the =
above defines an anonymous function (called a lambda function in computer science), which can be used in place without giving it a name as we did here. We’ll have examples of doing this later on.
As in most languages, input arguments and variables defined within a function have scope limited to the function itself. However, they can access values defined within an enclosing scope. For instance:
mycfun = lambda x : np.exp(c * np.sin(x))
c = 1; print(mycfun(3)) # exp(1*sin(3))
c = 2; print(mycfun(3)) # exp(2*sin(3))
There’s a lot more to be said about functions in Python, but this is enough to get started.
1.3.3Exercises¶
⌨ Write a function
poly1(p)
that returns the value of a polynomial at . You should do this directly, not by a call to or imitation of Function 1.3.1. Test your function on and .⌨ In statistics, one defines the variance of sample values by
Write a function
samplevar(x)
that takes as input a vectorx
of any length and returns as calculated by the formula. You should test your function on the vectorsones(100)
andrand(200)
. If you enterusing Statistics
in Julia, then you can compare to the results of thevar
function.⌨ Let
x
andy
be vectors whose entries give the coordinates of the vertices of a polygon, given in counterclockwise order. Write a functionpolygonarea(x,y)
that computes and returns the area of the polygon using this formula based on Green’s theorem:Here is the number of polygon vertices, and it’s understood that and . (Note: The function
abs
computes absolute value.) Test your functions on a square and an equilateral triangle.