A Closer Look at Programming

Classnotes for CPSC 215

C. Jacob — March (25^th) to April (5^th),  2002.

An Overview of Programming Styles

Recursion, Loops, and Functionals

We will now have a look at the most important tools for programming. This will show us that it is possible to program using different methodologies. Often the well-known procedural style, used in C, Fortran, or Pascal, is not the clearest nor the most efficient for Mathematica.

Recursive Programming

Recursive Example 1: Factorial Function

Let us have a look at the factorial function, which is defined as follows:

\(TraditionalForm\`fac(
          n) = \(\(n×\((n - 
                    1)\)× ... \)×2×1 = \(\(\(Π\)\(\ \)\)\+\(i = 1\)\%n\) i\)\)

A first, straight-forward implementation of this in Mathematica would be to use the built-in Product function, and even use the convenient mathematical notation:

fac[n_] := (∏_(i = 1)^n) i

fac[3]

But we can also look at this function from a slightly different perspective by making the following observation:

\(TraditionalForm\`fac(
          n) = \(n×\(\(\(\((n - 1)\)\(×\)\) ... \)×2×1\)\+\(\(fac(n - 1)\)\+⎵\) = \(n×\(fac(
                n - 1)\) = 
            n×\((n - 1)\)×\(fac(
                n - 2)\)\)\)
	\)
\(TraditionalForm\`	\(\(=\)\(n×\(\((n - 1)\)×\(fac(n - 2)\)\)\+\(\(fac(n - 1)\)\+⎵\)\)\)
	\)
\(TraditionalForm\`	\(\(=\)\(\ \)\(n×\((n - 
              1)\)×…×1×\(fac(0)\)\)\)\)

This allows the following recursive program for the factorial function:

facRec[n_Integer?NonNegative] := n facRec[n - 1]

facRec[0] = 1;

facRec[10]

The command

?facRec

shows us the order in which the definitions are applied by Mathematica. We see that they are ordered such that the more specific definition is used before the more general one. This assures that the end condition is reached–and the algorithm terminates.

Large recursions can trigger an internal security limit.

facRec[300]

If the recursion will terminate for sure, the limit can be enlarged (or even set to Infinity) by changing the global variable $RecursionLimit.

$RecursionLimit = 10^3;

facRec[300]

So, one has to be aware that there are certain limits in making use of recursive programming. However, writing functions, with an immediately recognizable recursive structure, down in a recursive manner seems very easy and natural to do.

Let us look at yet another example (the Fibonacci numbers), now with a double recursion.

Recursive Example 2: Fibonacci Numbers

Another example is the recursive calculation of Fibonacci numbers, using a function fib(…):

fib(0) = 0

fib(1) = 1

fib(n) = fib(n - 1) + fib(n - 2), for n > 1.

Here is how we can implement this function in a straight-forward and recursive manner:

fibRec[0] = 0;
fibRec[1] = 1;
fibRec[n_Integer?NonNegative] := 
      fibRec[n - 1] + fibRec[n - 2]

fibRec[4]

Let us follow the computations of this function step-by-step for a specific number:

fib[2]
	⟶ fib[1] + fib[0]
	⟶     1      +    0
	⟶     1

OK, that's easy. How about a higher number:

fib[4]
⟶ fib[3] + fib[2]
⟶ fib[2]  +  
        fib[1]  + fib[1] + fib[0]
⟶ fib[1]  +  
        fib[0] + 1 + 1 + 0
⟶ 1 + 0 + 2
⟶ 3

But this implementation is useless because the time needed to calculate fib for larger arguments grows exponentially.

t = Table[Timing[fibRec[n]], {n, 0, 20}]

ListPlot[Map[First, t /. {Second -> 1}], 
        PlotJoined -> True];

This happens because new recursions begin on the right-hand side of the definition for each summand, thereby calculating the same values over and over again. You can see this in the above example, where for fib[4] the value of fib[2] is calculated twice. When you look at fib[5], you can see this effect even more clearly:

fib[5]
⟶ fib[4] + fib[3]
⟶ fib[3] + fib[2] + 
        fib[2] + fib[1]
⟶ fib[2] + fib[1] + 
        fib[1] + fib[0] + fib[1] + fib[0] + 1
⟶ fib[1] + fib[0] + 
        1 + 1 + 0 + 1 + 0 + 1
⟶ 1 + 4
⟶ 5

fibRec[5]

or look at fib[6]:

fib[6]
⟶ fib[5] + fib[4]
⟶ fib[4] + fib[3] + 
        fib[3] + fib[2]
⟶ fib[3] + fib[2] + 
        fib[2] + fib[1] + fib[2] + fib[1] + fib[1] + fib[0]
⟶ fib[2] + fib[1] + 
        fib[1] + fib[0] + fib[1] + fib[0] + 1 + fib[1] + fib[0] + 1 + 1 + 0
⟶ fib[1] + fib[0] + 
        1 + 1 + 0 + 1 + 0 + 1 + 1 + 0 + 2
⟶ 1 + 0 + 7
⟶ 8

fibRec[6]

?fibRec

Fibonacci Calculations with Dynamically Stored Results

Here we present a solution to the problem that in recursive programming certain values are calculated over and over again. In order to avoid recalulations of already calculated values we keep track of all occuring calculations.

The following implementation stores the already calculated values dynamically. This speeds up the calculation.

fibDyn[0] = 0;
fibDyn[1] = 1;
fibDyn[n_Integer?NonNegative] := fibDyn[n] = 
        fibDyn[n - 1] + fibDyn[n - 2]

fibDyn[6]

?fibDyn

The efficiency—that is the time needed to for the calculations—of both variations is dramatically different. (For a fair comparison, values which have already been calculated for fibDyn must first be cleared.)

Clear[fibDyn];
fibDyn[0] = 0;
fibDyn[1] = 1;
fibDyn[n_Integer?NonNegative] := fibDyn[n] = 
        fibDyn[n - 1] + fibDyn[n - 2]

Timing[fibRec[26]]

Timing[fibDyn[26]]

In the following section, we will discuss procedural implementations which increase the efficiency of the programs, but reduce their readability.

Procedural Programming with Loops and Branches

Local Variables

If programs are to be given to users there lies the danger of collision between the names in the program and the names chosen by its user.

There are two mechanisms for avoiding such name collisions in Mathematica.

■ On the procedural or functional level, the Module mechanism is used to define local variables.

■ The technique used on a global level, especially for the names of the functions themselves, will be discussed in Section 4.4.5 (Contexts).

The Module function has two arguments. The first is a list of local variables, the second a possibly compound expression (a series of single expressions divided by semicolons). Against intuition, commas divide more strongly than semicolons.

The following function calculates the rotation of a planar vector by an angle φ. It makes sense to avoid multiple calculations of trigonometric functions, because this is time-intensive. With the help of two local variables, we can write:

\(rot2D[{x_, y_}, φ_] := 
	
      Module[{sinφ, cosφ}, 
		sinφ = 
          Sin[φ]; 
		cosφ = 
          Cos[φ]; 
		{{cosφ, \(-sinφ\)}, {sinφ, cosφ}} . {x, y}
	]\)

rot2D[{1, 1}, Pi/2]

The local variables can be initialized as they are introduced by using immediate definitions. This gives us a more compact implementation.

rot2D[{x_, y_}, φ_] := 
      Module[{sinφ = Sin[φ], 
          cosφ = 
            Cos[φ]}, {{cosφ, -sinφ}, {sinφ, cosφ}} . {x, y}]

If the result of a Module function is not calculated at the end of the module, it can be returned using Return, which is useful to apply within loops (see below).

The related functions With and Block, which also help to define local variables, will not be discussed here.

Loops and Branches (Conditionals)

For procedural programming Mathematica contains

the loops Do, While, For and

the branches If, Which, Switch .

Do and While can be useful. The For loop is a concession to C programmers; it often leads to badly structured programs.

Do - loop

Do is analogous to Table, except that it does not yield a result. We illustrate such a loop with the Print function.

Do[Print[1/x], {x, 5}]

Procedural (or iterative) Implementation of the Fibonacci Numbers

The following little program calculates Fibonacci numbers with a Do loop, by beginning with the first two (0 and 1) and calculating the higher ones by adding the two preceding ones.

fib(0) = 0

fib(1) = 1

fib(n) = fib(n - 1) + fib(n - 2), for n > 1.

By working with a list containing the values of two successive Fibonacci numbers, we arrive at a very elegant program. It is more efficient than the recursive ones regarding memory and compute time. On the other hand, with the recursive program, we can immediately see how it works, whereas here we have to think about it a bit first.

fibIter[
        n_] := 
Module[
{fn1 = 0, 
          fn2 = 1}, 
Do[{fn1, fn2} = {fn1 + fn2, 
              fn1}, {n}]; 
fn1
]

Let us, again, follow the (recursive) computations of the fib function in a step-by-step manner for a specific number (compare the previous examples):

fib[4]
⟶ fib[3] + fib[2]
⟶ fib[2]  +  
        fib[1]  + fib[1] + fib[0]
⟶ fib[1]  +  
        fib[0] + 1 + 1 + 0
⟶ 1 + 0 + 2
⟶ 3

And here is how the iterative function fibIter implements this dynamic behaviour:

{(Subscript[fn, 1]), (Subscript[fn, 2])} = {0, 1}
(Overscript[⟶, 1]) {(Subscript[fn, 1]), 
            (Subscript[fn, 2])} = {(Subscript[fn, 1]) + (Subscript[fn, 2]), 
            (Subscript[fn, 1])} = {0 + 1, 0} = {1, 0}
(Overscript[⟶, 2]) {(Subscript[fn, 1]), 
            (Subscript[fn, 2])} = {(Subscript[fn, 1]) + (Subscript[fn, 2]), 
            (Subscript[fn, 1])} = {1 + 0, 1} = {1, 1}
(Overscript[⟶, 3]) {(Subscript[fn, 1]), 
            (Subscript[fn, 2])} = {(Subscript[fn, 1]) + (Subscript[fn, 2]), 
            (Subscript[fn, 1])} = {1 + 1, 1} = {2, 1}
(Overscript[⟶, 4]) {(Subscript[fn, 1]), 
            (Subscript[fn, 2])} = {(Subscript[fn, 1]) + (Subscript[fn, 2]), 
            (Subscript[fn, 1])} = {2 + 1, 2} = {3, 2}

Let us look at a short calculation and its traced intermediate steps, which correspond with the calculation steps above:

Trace[fibIter[4]]

This implementation is fast, as it does not need to store recursive calculations:

fibIter[200]

t = Table[Timing[fibIter[n]], {n, 0, 200}]

ListPlot[Map[First, t /. {Second -> 1}], 
        PlotJoined -> False];

There are even faster methods for calculating Fibonacci numbers. The built-in function Fibonacci uses such an algorithm.

Timing[fibIter[20000]]

Timing[Fibonacci[20000]]

While - loop

The While function uses a test as the first argument, and as the second argument a compound expression (single expressions divided by semicolons). Here the various possibilities for manipulating iteration variables (see Section 2.4.4 of the Mathematica Book) can be useful, for example ++.

Module[{n = 1, t}, 
t = n; 
While[
        n <= 4, t = 
          x + 1/t; n++]; 
t
]

If necessary the program flow may be controlled using Return, Continue, Break, and Catch/Throw.

Here is another version for the Fibonacci numbers, now with a While loop:

fibWhile[
        n_] := 
Module[{fn1 = 0, fn2 = 1, 
          i = 1}, 

While[
          i <= n, {fn1, fn2} = {fn1 + fn2, 
              fn1}; i++]; 
fn1
]

fibWhile[4]

fibWhile[200]

For - loop

The For loop is another way of formulating loop statements.

The For loop is a concession to C programmers; it often leads to badly structured programs!

The For loop has the general structure For[start, test, incr, body].

For[i = 0, i < 5, i++, Print[i]]

Here is the same example, a little commented and streched out:

For[
i = 0,            
i < 5,            
i++,              
Print[i]      
]

Yet another implementation for the Fibonacci numbers, now with a For loop:

fibFor[
        n_] := 
Module[{fn1 = 0, 
          fn2 = 1}, 
For[
i = 
            1,     (* start value for loop *) 
i <= 
            n,   (* 
            test whether to continue loop *) 
i++,       (* 
            increment loop counter *) 
{fn1, 
              fn2} = {fn1 + fn2, fn1}]; 
        fn1     (* body *) 
]

fibFor[4]

fibFor[200]

If - branch

If can process two to four arguments: If[condition, ifTrue, ifFalse, otherwise].

Table[
If[
        PrimeQ[n], 
n, 
{n, 
          FactorInteger[n]}], 
{n, 2, 10, 
        2}
]

We could achieve the same result with a pure function:

Map[If[PrimeQ[#], #, {#, FactorInteger[#]}] &, 
      Range[2, 10, 2]]

Here is the same example, where the resulting numbers are embedded in expressions that tell more about the checked numbers (prime, noprime, factors).

t = 
      Table[
If[
          PrimeQ[n], 
prime[
            n], 
{noprime[n], 
            factors @@ FactorInteger[n]}], 
{n, 2, 
          10}
]

With this more elaborate representation it is easier to extract specific expressions, e.g.,

■ all primes:

Cases[t, _prime] /. {prime[x_] -> x}

■ all non-primes:

Cases[t, _noprime, ∞] /. {noprime[x_] -> x}

■ all factors:

Cases[t, _factors, ∞] /. {factors -> List}

In the If[condition, ifTrue, ifFalse, otherwise] the variation otherwise handles cases where the test function does not evaluate to either True or False:

\(\(If[
          NonNegative[#], 
"nonnegative", 
"negative", 
"unknown"
] &\)\  /@ \ {\(-1\), 0, 1, a}\)

Fibonacci Numbers again: While and If

Using the If statement, we can reformulate our examples for calculating the Fibonacci numbers.

First, here is the original implementation:

fibWhile[
        n_] := 
Module[{fn1 = 0, fn2 = 1, 
          i = 1}, 

While[
          i <= n, {fn1, fn2} = {fn1 + fn2, 
              fn1}; i++]; 
fn1
]

... and here is the reformulation with an If statement embedded in an infinite While loop. We also have to use the Return command to jump out of the loop.

fibWhileIf[
        n_] := 
Module[{fn1 = 0, fn2 = 1, 
          i = 1}, 

While[
          True, 
If[
            i <= n, 
{fn1, fn2} = {fn1 + fn2, 
                fn1}; i++, 
Return[
              fn1]
]
]
]

fibWhileIf[4]

Fibonacci Numbers again: Recursive and If

Here is how we implemented the Fibonacci numbers in a recursive manner:

fibRec[0] = 0;

\(\(fibRec[1] = 1;\)
\)
fibRec[n_Integer?NonNegative] := 
      fibRec[n - 1] + fibRec[n - 2]

We can reformulate this implementation by using If statements:

fibRecIf[n_Integer?NonNegative] := 
If[n == 0, 
        0, 
If[n == 1, 
          1, 
fibRecIf[n - 1] + 
            fibRecIf[
              n - 2]
]
]

Obviously, this leads to a program that is not as easy to understand at the very first look, but it leads to the same results:

fibRecIf[4]

We can make the program a little easier, getting rid of the nested If statements by using the Which command discussed in the following section.

Which - branch

Which processes an even number of arguments where every test is followed by the result that must be returned if the test yields True. The tests are processed from left to right until the first True.

Example 1: Intervals function

Let us look at the function

intervals[x_] = 
Which[
x < 0, 
          0, 
x < 1, 1, 
x < 2, 
          2
];

and evaluate it for the elements of the following list:

intervals /@ {- .5,  .5, 1.5, 2.5}

Values ⩾ 2 are not anticipated by this function: Which yields the symbol Null. We can catch such exceptions by entering True as the last test.

\(\(intervalTest[x_] = 
Which[
x < 0, 
          0, 
x < 1, 1, 
x < 2, 
          2, 
True, "outside"
];\)\)

intervalTest /@ {- .5,  .5, 1.5, 2.5}

The Which function provides a convenient shortcut for nested If-statements. The following is essentially the same interval test function as above, but with nested If-statements:

intervalTestIf[x_] = 
If[x < 0, 
          0, 
If[x < 1, 
            1, 
If[x < 2, 
              2, 
"outside"]]
];

intervalTestIf /@ {- .5,  .5, 1.5, 2.5}

Example 2: Fibonacci revisited

Let us come back to our implementation of the Fibonacci function with nested Ifs:

fibRecIf[n_Integer?NonNegative] := 
If[n == 0, 
        0, 
If[n == 1, 
          1, 
fibRec[n - 1] + 
            fibRec[n - 2]
]
]

Now that we know how to use Which to replace nested If conditions, we can reformulate this function:

fibRecWhich[
        n_Integer?
          NonNegative] := 
Which[
n == 0, 
        0, 
n == 1, 1, 
True, 
        fibRecWhich[n - 1] + 
          fibRecWhich[n - 2]
]

fibRecWhich[4]

Switch - branch

Switch tests a given expression on patterns. After the expression, pairs of patterns and the corresponding results follow. Here the exceptions can be caught with a blank.

analyze[x_] := 
Switch[
        x, 
_^2, "quadratic", 
_^3, "\
cubic", 
_, "other"
]

analyze /@ {a, a^2, a^3, a^6}

Back to our Fibonacci function, here is an implementation with Switch:

fibRecSwitch[n_Integer?NonNegative] := 
Switch[
        n, 
0, 0, 
1, 
        1, 
_, 
        fibRecSwitch[n - 1] + 
          fibRecSwitch[n - 2]
]

fibRecSwitch[4]

Functional Programming

Mathematica is predestined for functional programming. This means the nesting of functions, as we have done a lot of times without thinking about it. Let us look at a class of problems which are well-suited as examples for functional programming.

Many nonlinear algorithms can be reduced to the search for fixed points of mappings. One starts at any initial point and applies the mapping. The same mapping is again applied to the result. And so on, until the difference between two successive results is within a chosen threshold.

A good example is Newton’s algorithm for finding roots of functions. We consider a function of one variable and begin with an initial x value. At this point we draw the tangent to the graph and determine its intersection with the x axis. This gives us the first approximation of the root.

Let us choose the function

f[x_] = Cos[(x^2)] - Sin[x];

Plot[f[x], {x, 0, 2}];

as an example. We need its derivative.

df[x_] = (∂_x) f[x]

The initial value shall be (Subscript[x, 0])=1.6. The tangent therefore looks like this:

The intersection with the abscissa calculates as (Subscript[x, 0])-((f((Subscript[x, 0])))/(f'((Subscript[x, 0])))). The corresponding pure function is:

# - f[#]/df[#] &

Applied to the initial value, it yields:

# - f[#]/df[#] &[1.6]

With this value the same procedure is repeated to determine the second approximation of the root. With the exception of pathological cases (horizontal tangents, limit cycles) we very quickly arrive at a good approximation of the root. The Mathematica function Nest is very helpful for this process:

Nest[g, x, 5]

NestList can be used to see the intermediate results as well:

NestList[g, x, 3]

In place of g we simply have to use our pure function–and the program is finished.

NestList[# - f[#]/df[#] &, 1.6, 5]

The nesting functions FixedPointList and FixedPoint work until two successive results agree (within a built-in threshold defined by SameQ):

FixedPointList[# - f[#]/df[#] &, 1.6]

FixedPoint[# - f[#]/df[#] &, 1.6]

(In Mathematica 4, the functions NestWhile and NestWhileList give you extended control over the tests used to terminate the nesting.)

By using local variables, we could program Newton’s algorithm as follows:

Clear[f, df]

myNewton[f_, {x_, x0_}] := 
      Module[{df = (∂_x) f}, 
        FixedPoint[#1 - (f/df) /. x -> #1 &, 
          N[x0]]]

myNewton[Cos[(x^2)] - Sin[x], {x, 1.6}]

The functions FoldList, MapIndexed, and ComposeList are also helpful in functional programs. They allow iterations over lists:

FoldList[f, x, {a, b, c}]

MapIndexed[f, {a, b, c}]

ComposeList[{f1, f2, f3}, x]

Fibonacci numbers revisited

Here is a functional version for calculating the Fibonacci numbers.

We derive our functional implementation from our previous iterative definition:

fibIter[
        n_] := 
Module[{fn1 = 0, 
          fn2 = 1}, 
Do[{fn1, fn2} = {fn1 + fn2, 
              fn1}, {n}]; 
fn1
]

Here is the "translation" into a functional program using Nest:

fibNest[
        n_] := 
Nest[{#[[1]] + #[[2]], #[[1]]} &, {0, 1}, n] // First

fibNest[4]

A very inefficient but working definition using ComposeList is the following:

ComposeList[{f1, f2, f3}, x]

fibComposeList[
        n_] := 
ComposeList[
            Table[{#[[1]] + #[[2]], #[[1]]} &, {n}], {0, 1}] // Last // First

fibComposeList[4]

Many Ways to Do the Same ...

Variations on Writing Programs

In this section we will explore a few examples of how to implement the same functionality in various ways, most of them having their pros and cons, some being more apt to the way Mathematica works, and other being more close to the style of programming in traditional programming languages, such as C, C++, Java, Basic, or Pascal.

9+ Ways of Implementing the Fibonacci Function

Here is a brief overview of the implementations of the Fibonacci function,

fib(0) = 0

fib(1) = 1

fib(n) = fib(n - 1) + fib(n - 2), for n > 1,

which we discussed in the previous sections.

Recursive Versions

(1) Immediate recursive definition

fibRec[0] = 0;
fibRec[1] = 1;
fibRec[n_Integer?NonNegative] := 
      fibRec[n - 1] + fibRec[n - 2]

(2) Recursion with dynamic storage

fibDyn[0] = 0;
fibDyn[1] = 1;
fibDyn[n_Integer?NonNegative] := fibDyn[n] = 
        fibDyn[n - 1] + fibDyn[n - 2]

(3a) Recursion with conditions: If to replace the outer pattern matching

fibRecIf[n_Integer?NonNegative] := 
If[n == 0, 
        0, 
If[n == 1, 
          1, 
fibRecIf[n - 1] + 
            fibRecIf[n - 2]]]

(3b) Recursion with conditions: Which to replace the outer pattern matching

fibRecWhich[
        n_Integer?
          NonNegative] := 
Which[
n == 0, 
        0, 
n == 1, 1, 
True, 
        fibRecWhich[n - 1] + 
          fibRecWhich[n - 2]
]

(3c) Recursion with conditions: Switch to replace the outer pattern matching

fibRecSwitch[n_Integer?NonNegative] := 
Switch[
        n, 
0, 0, 
1, 
        1, 
_, 
        fibRecSwitch[n - 1] + 
          fibRecSwitch[n - 2]
]

Iterative Versions

(4) Iteration with a Do loop

fibIter[
        n_] := 
Module[{fn1 = 0, 
          fn2 = 1}, 
Do[{fn1, fn2} = {fn1 + fn2, 
              fn1}, {n}]; 
fn1
]

(5) Iteration with a While loop

fibWhile[
        n_] := 
Module[{fn1 = 0, fn2 = 1, 
          i = 1}, 
While[
          i <= n, {fn1, fn2} = {fn1 + fn2, 
              fn1}; i++]; 
fn1
]

(6) Iteration with a For loop

fibFor[
        n_] := 
Module[{fn1 = 0, 
          fn2 = 1}, 
For[i = 1, 
          i <= n, i++, 
{fn1, fn2} = {fn1 + fn2, fn1}]; 
        fn1 
]

(7) Iteration with a While loop and an If condition

fibWhileIf[
        n_] := 
Module[{fn1 = 0, fn2 = 1, 
          i = 1}, 
While[
          True, 
If[
            i <= n, 
{fn1, fn2} = {fn1 + fn2, 
                fn1}; i++, 
Return[
              fn1]]]
]

Functional Versions

(8) Functional definition with Nest

fibNest[
        n_] := 
Nest[{#[[1]] + #[[2]], #[[1]]} &, {0, 1}, n] // First

(9) Functional definition with ComposeList

fibComposeList[
        n_] := 
ComposeList[
            Table[{#[[1]] + #[[2]], #[[1]]} &, {n}], {0, 1}] // Last // First

Of course, these are only a few examples to demonstrate that there are many, many different ways of implementing the same function. In most cases these programs differ in
	□ efficiency (calculation time and memory space)
	□ length
	□ readability
	□ comprehension
	□ ...

15+ Ways of Implementing the Factorial Function

Just to repeat, the factorial function is defined as follows:

\(TraditionalForm\`fac(
          n) = \(\(n×\((n - 
                    1)\)× ... \)×2×1 = \(\(\(Π\)\(\ \)\)\+\(i = 1\)\%n\) i\)\)

(1) Using the built-in Mathematica factorial function

f = Factorial

(2) This, of course, is a short way of defining the function like this:

f[n_] := Factorial[n]

(3) ... or we use a different notation for the Factorial function

f[n_]  :=  n!

(4) A recursive definition, that implements

\(TraditionalForm\`fac(
          n) = \(n×\(\(\(\((n - 1)\)\(×\)\) ... \)×2×1\)\+\(\(fac(n - 1)\)\+⎵\) = 
          n×\(fac(n - 1)\)\)\)
\(TraditionalForm\`	\(\(=\)\(n×\(\((n - 1)\)×\(fac(n - 2)\)\)\+\(\(fac(n - 1)\)\+⎵\)\)\)\)
\(TraditionalForm\`	\(\(=\)\(\ \)\(n×\((n - 
              1)\)×…×\(fac(1)\)\)\)\)

is this:

f[n_]  :=  n  f[n - 1] ;  f[1]  =  1

(5) Here's another recursive solution, now using an If branching statement:

f[n_]  :=  If[n  ==  1,  1,  n f[n - 1]]

(6) However, this can also be implemented in an iterative programming style, using a Do loop:

f[n_]  :=  
      Module[{t  =  1},  Do[t  =  t*i,  {i,  n}];  t]

(7) And we can achieve the same using a For loop:

f[n_]  :=  
      Module[{t  =  1,  i},  
        For[i  = 1,  i  <= n,  i++,  t  *=  i];  t]

(8) We can even use a While loop:

f[n_]  :=  
      Module[{t  =  1, i = 1},  While[i <= n, t  =  t*i; i++];  
        t]

(9) As the factorial function involves the calulation of products, we can use the Product function. This is more of a functional implementation:

f[n_]  :=  Product[i,  {i,  n}]

(10) ... which we can also write as follows:

f[n_] := (∏_(i = 1)^n) i

(11) Now here is a more elegant functional solution, using the Apply function:

f[n_]  :=  Apply[Times,  Range[n]]

(12) Or another functional implementation, using the Fold function:

f[n_]  :=  Fold[Times,  1,  Range[n]]

(13) Then there is a generalized factorial function, called the Gamma function:

f[n_]  :=  Gamma[n - 1]

(14) And there are definitions using pure functions, like this

f  =  If[#1  ==  1,  1,  #1 #0[#1  -  1]] &

(15) ... and here is a rather complicated pure function definition:

f[n_]  :=  
      Fold[#2[#1] &,  1,  Array[Function[t,  #  t] &,  n]]

How does this function work?

Array[Function[t,  #  t] &,  3]

Finally, we clear all these definitions for f:

Clear[f]

12+ Ways of Implementing the Summation Function

We are now looking at a quite similar function:

\(TraditionalForm\`s(
          n) = \(\(n + \((n - 1)\) +  ... \) + 2 + 
            1 = \(\(\(Σ\)\(\ \)\)\+\(i = 1\)\%n\) i\)\)

(1) As the summation function involves the calulation of sums, we can use the Sum function. This is more of a functional implementation:

s[n_]  :=  Sum[i,  {i,  n}]

(2) ... which we can also write as follows:

s[n_] := (∑_(i = 1)^n) i

(3) A recursive definition, that implements

\(TraditionalForm\`s(
          n) = \(n + \(\(\(\((n - 1)\)\(+\)\) ... \) + 2 + 1\)\+\(\(s(n - 1)\)\+⎵\) = n + s(n - 1)\)\)
\(TraditionalForm\`	\(\(=\)\(n + \(\((n - 1)\) + s(n - 2)\)\+\(\(s(n - 1)\)\+⎵\)\)\)\)
\(TraditionalForm\`	\(\(=\)\(\ \)\(n + \((n - 1)\) + … + 
          s(1)\)\)\)

is this:

s[n_]  :=  n  +  s[n - 1] ;  s[1]  =  1;

(4) Here's another recursive solution, now using an If branching statement:

s[n_]  :=  If[n  ==  1,  1,  n +  s[n - 1]]

(5) However, this can also be implemented in an iterative programming style, using a Do loop:
Note that we have to initialize t = 0:

s[n_]  :=  
      Module[{t  =  0},  Do[t  =  t + i,  {i,  n}];  t]

(6) And we can achieve the same using a For loop:

s[n_]  :=  
      Module[{t  =  0,  i},  
        For[i  = 1,  i  <= n,  i++,  t  +=  i];  t]

(7) We can even use a While loop:

s[n_]  :=  
      Module[{t  =  0, i = 1},  While[i <= n, t  =  t + i; i++];  
        t]

(8) Now here is a more elegant functional solution, using the Apply function:

s[n_]  :=  Apply[Plus,  Range[n]]

(9) Or another functional implementation, using the Fold function:

s[n_]  :=  Fold[Plus,  0,  Range[n]]

(10) And there are definitions using pure functions, like this (this is actually recursive, although not obviously so):

s  =  If[#1  ==  1,  1,  #1  + #0[#1  -  1]] &;

(11) ... and here is a rather complicated pure function definition:

s[n_]  :=  
      Fold[#2[#1] &,  0,  Array[Function[t,  # + t] &,  n]]

Finally, we clear all these definitions for s:

Clear[s]