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
Example 1.3.2
Here we show how to use Function 1.3.1 to evaluate a polynomial. It’s not a part of core Julia, so you need to download and install this text’s package once, and load it for each new Julia session. The download is done by the following lines.
#import Pkg
#Pkg.add("FNCBook");
Once installed, any package can be loaded with the using
command, as follows.
Tip
Many Julia functions, including the ones in this text, are in packages that must be loaded via using
or import
in each session. Sometimes a using
statement can take a few seconds or even minutes to execute, if packages have been installed or updated.
#using FundamentalsNumericalComputation
using FNCFunctions
FNC = FNCFunctions
For convenience, this package also imports many other packages used throughout the book and makes them available as though you had run a using
command for each of them.
Tip
If you are not sure where a particular function is defined, you can run methods
on the function name to find all its definitions.
Returning to horner
, let us define a vector of the coefficients of , in ascending degree order.
c = [-1, 3, -3, 1]
4-element Vector{Int64}:
-1
3
-3
1
In order to avoid clashes between similarly named functions, Julia has boxed all the book functions into a namespace called FNC
. We use this namespace whenever we invoke one of the functions.
Tip
You must use the module name when a package is loaded by import
, but when loaded via using
, some functions may be available with no prefix.
FNC.horner(c, 1.6)
0.21600000000000041
The above is the value of .
While the namespace does lead to a little extra typing, a nice side effect of using this paradigm is that if you type FNC.
(including the period) and hit the Tab key, you will see a list of all the functions known in that namespace.
The multi-line string at the start of Function 1.3.1 is documentation, which we can access using ?FNC.horner
.
matlab
Example 1.3.2
Here we show how to use horner
to evaluate a polynomial. First, we have to ensure that the book’s package is imported.
import fncbook as FNC
Here is the help string for the function:
help(FNC.horner)
Help on function horner in module fncbook.chapter01:
horner(c, x)
horner(c,x)
Evaluate a polynomial whose coefficients are given in descending order
in c, at the point x, using Horner's rule.
We now define a vector of the coefficients of , in descending degree order. Note that the textbook’s functions are all in a namespace called FNC
, to help distinguish them from other Python commands and modules.
c = array([1, -3, 3, -1])
print(FNC.horner(c, 1.6))
0.21600000000000041
The above is the value of , up to a rounding error.
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.
If an executing function reaches its end
statement without encountering a return
statement, then the output is undefined, which is a common source of bugs.
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¶
The exercises marked with a computer icon require the use of a computer. The others can be done by hand.
A polynomial is represented as a vector of coefficients in all three languages covered by this book. However, in Julia they are given in ascending degree order, which is most convenient programmatically, while in MATLAB and NumPy they are given in descending order, which is the way we usually write them. This difference makes writing the exercises universally a little awkward, so be advised.
- ⌨ 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.