Recursion Versus Iteration with the List as a Data Structure

A reversible sequence of steps from the specification of the algorithm and the mathematical definition of the recurrent solution through the recursive procedure, the tail recursive procedure and finally to the iteration procedure, is shown. The notation for analysing recursive function execution as well as modified flow charts of an algorithm to identify the differences between the iteration and the tail recursion are proposed. All the procedures are written in Logo, so the lists are used as the data structure. Transformation from the recursive procedure to the iterative procedure and vice versa can be shown in such a way in every language in which the recursion is allowed. All examples are one-recursion-call examples and all except one are the functions of discrete mathematics.


Introduction
This article deals with one of the main concepts of programming: iteration versus recursion.I describe a sequence of steps developed while working in computer labs with my students of the first year of the master's degree programme of "chemistry with informatics".Many of them are not well prepared or even unprepared by the secondary school to study informatics.The name of the course is "Algorithms and data structures" but in view of the students' level it must contain also the basic principles of programming.I decided to use Imagine Logo (Kalaš et al., 2001) as one of the tools.Logo is underestimated and easy to use educational tool, especially for recursion.The main difference between Logo and the other languages from the point of view of this article is that Logo operates on list, similarly to all dialects of Lisp.While working with students I realized the necessity of using one well-defined and convenient way of notation for analysis of the procedure working.I think that the meaning of the use of any notations (and annotations) is out of question not only for me.For example (McCartney et al., 2005) wrote: "any annotation was better than none".I would extend it to say that a good notation is of key importance (I do hope to be an exception from the rule to leave well alone).There are a lot of notations and the problem is which one to suggest to our students, to become the most convenient, clearest, unique and fruitful (I assume that there really exist one notation that is good for all).Finally I have come to an effective way of notation which, in my opinion, fulfilled these criteria, and employed it successfully at my lab classes.As follows from my experience, it is better to suggest one good way of notation at the beginning of the course than to struggle with a lot of other ideas later.In the tests and exam's questions I used to put blank tables to be filled in to avoid ambiguity.Certainly I must remember that the sequence of steps I propose, as every method, has its own restrictions.In particular, this method works well for one-recursion-call examples.The work of the procedure in two-recursion-call examples will be shown in the next paper (Foltynowicz, 2007).
Every exercise starts from a specification of the algorithm: the definition of the inputs (parameters of a function) and outputs (returns) taking into regard the data structures.Than we formulate the mathematical recursive solution: base case and general, recursion case, and translate it into a function written in Logo.After this we ought to analyse the work of the recursive procedure and try to improve it by removing the collecting output phase at the expense of creating an additional accumulating parameter (in other words transform the recursion to the tail-end recursion).For the purpose of analysing the work of the recursive and tail recursive procedures I suggest the table with tree columns: the first for the logic value of the stop condition, the second for the recursive call phase and the third for the collecting output phase.The arrows shows the order of execution ruled by the stack (based on the principle Last In First Out = LIFO).The tail recursion (or tail-end recursion) is a special case of recursion that can be easily transformed into an iteration (for example 4).Replacement of the recursion with iteration, can drastically decrease the amount of stack space used and improve efficiency but is rather less intuitive than recursion.Every recursive version has an equivalent iterative version and vice versa.For the purpose of analysing the work of the procedure we make a table which needs to be well defined.The table notation for demonstrating the working of iterative algorithms is not novel and is shown for completeness and clarity of the whole process.The number of its columns is equal to the number of variable names used in the procedure with one additional column for logic values of the loop condition.The number of rows is equal to the number of values assigned to the variable by the assignment statement during the execution of the procedure for given data i.e. the number of rows is the number of iterations plus one (the first row is for the initial values before the loop).The order of filling the table is from left to right and must be the same as the order of execution of the statements in the algorithm.For explanation of the differences between the iterative and recursive versions of the function the modified flow charts for the functions are proposed.They begin from the name of the procedure (function) and the names of the formal parameters and end with the variable name returned by the function.The arrows show the direction of overwriting the subsequent values of actual procedure parameters.
Testing the execution of the function for given data it is worth discussing the place of putting in some checking code, which usually is the print statement of the values of the function parameters.The working of the method is illustrated in several simple and important examples of discrete mathematics.As follows from my experience, such a training helps to understand the interrelation between the mathematical definition, recursive and iterative procedures and flow chart, which are all different ways of algorithm notation.If we carry out the analysis of several examples in such an ordered way, we quickly discover the following two rules: 1.The loop condition for the most general (in the sense of being always possible to use) while loop is a logical negation of the base case in recursion.2. In the recursive version the assignment statements of iterative version are replaced by exchanging subsequent values of actual procedure parameters (it should be emphasised that these values are overwritten: the next on the previous one as in the assignment statement).
It sounds like conclusions of the paper, but I decided to place them in the introduction because these are the conclusions for learners, not for teachers, and they ought to be discovered by students.Discovering is more time consuming and more valuable then suggesting the rules at the beginning of the process.
Regularities and invariants which, without any doubts, are essential for considerations of both correctness and efficiency (Ginat, 2003) will be only mentioned at the level of beginners.
I hope that the method of transforming a recursive function into an iterative one, shown below, will turn to be useful for every teacher.All algorithms can be executed for small integer numbers so all examples are good for written exercises and tests done without help of a calculator or a computer.In the lab classes the use of a computer is obligatory for trying and checking.
The first one-recursion-call example, factorial of a number, the most frequently used in literature, is treated as the model and is described in detail.

Specification of the algorithm:
Input: integer number n 0. Output: n! 2. Mathematical definition of recursive solution: (1) 3. Logo recursive procedure and its work: where op means output, procedure is the function which gives the value as the output (the same as return in pseudocode and in most of programming languages).The calling of the procedure and the subsequent result are shown below: ? show FACTOR 5 120 In Table 1 trace of the procedure execution for the actual parameter n = 5 is placed.Stop condition comes from the base case.The arrows shows the order of execution.The rule is simple: what cannot be executed must be written symbolically onto the stack (recursive call column) and after reaching the stop condition it must be executed (collecting output phase column).The recursive call phase column shows symbolically that the recursive call requires the compiler to allocate storage on the stack at run-time for every call that has not yet returned.
The procedures equivalent to the procedure ( 2), but written in a slightly different way, are shown only in Table 2.Such differences in notation do not introduce any new information to the subject of this work, and will therefore not be considered further.
4. Since the transformation of the recursion into tail recursion implies an introduction of an additional (accumulating) parameter into the function, the question arises on the initial value of this parameter.In other words: we must answer the question what is the value of the parameter this function must be called with: Mathematical definition: 5. Logo tail recursive procedure and its work: ?show FACTORp 5 1 120 Nothing has to be done after the recursive call, the solution is collected in the additional parameter.The tail recursive function is less memory consuming then the recursive one, there is no collecting output phase during the execution and usually the compiler optimizes the tail recursive version to include the simple loop.
Table 3 Stop condition: :n = 0 Recursive call phase Collecting output phase

Recursion versus tail recursion:
In Table 4 two procedures (2) and (2p) are placed once more for exact comparison and some symbols are bold.It is worth watching carefully what happens: the operator (n *) acts on the additional parameter :f instead of the recursive call FACTOR :n -1, parameter :f is outputted instead of 1 in the base case: We used to say that tail recursion (or tail-end recursion) is a special case of recursion in which the last operation of the function is a recursive call.What does it mean?It means that if the recursive call is an element of the arithmetic formula (the case of FACTOR) or is the parameter of other procedure (the case of decbin for example; see below) the recursion is not tail recursion.Fig. 1 shows how the difference between recursion and tail recursion could be illustrated by the flow charts modified for functions.

Iterative version written in Logo.
to Factorit :n let "f 1 repeat :n [let "f :f * repc] op :f end In Imagine Logo in loops: repeat, for, while and forEach can be used repcount (short: repc).It outputs a positive integer, which is the number of repetitions.It begins from 1 Repcount behaves like a variable which is always increased by 1 in the body of the loop.Repeat loop is the most typical loop of Logo.

repeat :n [body of the loop which is the list of statements]
We must remember that the typical data structure in Logo is the list.It is worth emphasizing that there is no loop (which will terminate) without the loop condition, even if it is not explicitly written in the language.The loop condition for repeat loop is 1 <= repc <= :n of course.The procedure performance for n = 5 is shown in Table 5.
In Logo, in contrast to other popular programming languages, the repeat loop is more convenient than the for loop, because in the last one the end value of repeats cannot be the name of the variable, it must be a number.The way to overcome this difficulty is to make a list by the sentence (se) or list procedure.All alternative notations of the iterative procedure (2it) are collected in Table 6.

Iteration versus tail recursion
Iterative version was chosen in its most general version: with while loop (this kind of loop is possible to use in every case) and with loop condition being the logic negation of the stop condition from the recurrent version1 and placed below in the Table 8 with the tail recursive version for comparison.The performance of such procedure (FACTORit) is the same as the tail recursive one (FACTORp).Let us for example see (Table 7) how the procedure FACTORit is executed for the actual parameters: n = 5 and :f = 1: Fig. 2 shows how the difference between iteration and tail recursion could be illustrated by the flow charts modified for functions.Roughly speaking we can say that every loop (while, for, repeat) works "horizontally" and every recursion works "vertically".As a consequence, the statements placed between the beginning of the procedure and the loop condition are performed only once (they are placed outside the body of the loop) in contrast to the instruction placed between the beginning of the recursive procedure and the stop condition, which are performed as many times as the procedure is called by itself (they are placed inside the body of the loop).So we should not place the instruction that must not be repeated between the beginning of the recurrent procedure and the place of Tail recursion is the special case of recursion that is semantically equivalent to the iteration constructs normally used to represent repetition in programs.Because tail recursion is equivalent to iteration, tail-recursive programs can be compiled as efficiently as iterative programs.
In Appendix 1 four easy examples without structural data and two with the list as a data structure are presented in the form of a laboratory exercise.Euclidean algorithm for finding the greatest common divisor is very important but not typical: it is defined by tail-end recursion.

Arithmetic of Integers: Two Integer Functions div and mod
Although these functions are usually implemented in programming languages it is worth performing this task to understand better what they really are.
1. Specification of the algorithm: Inputs: two natural numbers a, and b Outputs: the quotient of an integer division a by b: a div b, and the remainder of division a by b: a mod b 2. Mathematical definition of recursive solution These two function are interrelated:
to divmod :a :b :q if :a < :b [op se :q :a] op divmod :a -:b :b :q + 1 end 5. Iterative version and its execution (Table 11) For clarity data a and b do not change, variable r is used for changing a.
to divmod :a :b let "q 0 let "r :a while [:r >= :b][let "q :q + 1 let "r :r -:b] let "L se :q :r op :L end In the Table 11 additionally the invariant (Kenneth and Wright, 1992) is shown.The idea of checking the correctness by inverting this algorithm is not good, because although the correspondences (a, b) → a div b and (a, b) → a mod b are unique (are two two-dimensional discrete functions, but not one-to-one functions), the invert correspondences are not functions at all (the inverse function of the two-dimensional not one-to-one function does not exist).It would be instructive to show to the students the plots of these two discrete functions but it is not easy to find a proper tool.The Excel 3-D Surface plot type of the functions f (x, y) = z = x div y and f (x, y) = z = x mod y could be well understood if we remembered that the sets of x, y and z contained discrete values.Compare the plots made in Excel with the ones made in Sigma-Plot, which are really discrete.I hope it helps understand what these function really are.

Specification of the algorithm:
Input: integer number n 1 (in decimal representation).
Output: string or list of bits being the binary representation of n (output is not a simple variable -it is structured).2. Mathematical definition of recursive solution: 3. Logo recursive procedure and testing its execution ( Using a string (Table 13): ? show decbinp 12 " 1100 Table 13 Stop condition: :n=1 Recursive call phase Collecting output phase ?decbinp 12 " 0 decbinp 6 0 0 decbinp 3 00 0 decbinp 1 100 1 1100 or using a list (Table 14): Table 14 Stop condition: :n=1 Recursive call phase Collecting output phase

Recursion versus tail recursion:
It is worth watching carefully what happens (Table 15).It can be done in a slightly different way (Table 16).
How to check whether the algorithms have been correctly implemented?The next algorithm provides a possible answer.

Specification of the algorithm:
Inputs: integer number n, list [a 0 a 1 . . .a n ], x (simple and structuralised variables); a 0 a 1 . . .a n and x are real numbers or integer numbers as a special case.a 0 a 1 . . .a n are the coefficients of the polynomial of degree n Output: real number w n (x), evaluation of the polynomial at x. 2. Mathematical definition of recursive solution: or with all data as parameters; in that case a is a set (list) of coefficients defining the polynomial and we should answer the question if it changes: 3. Logo recursive procedure and its execution (Table 18).
to Horner :n :x :a if :n = 0 [op first :a] op :x * (Horner :n -1 :x bl :a) + last :a end Let ours example be: ? show Horner 4 -2 [3 0 -4 4 -5] 19 Table 18 Stop condition: :n = 0 Recursive call phase Collecting output phase 4. The tail-end recursion.Let N be the degree of the polynomial (N is a constant) and n the parameter whose value changes from N to 0 (n is a variable): The initial value of parameter p can be 0. It is also possible to take the initial value of a as a list of coefficients without the first one and p as the a 0 (Table 20):  We can also conclude that for iterative solution it would be more convenient to use the opposite convention of writing coefficients to that used for recursive solution, namely: Using coefficients in array we do not remove one coefficient in every step, as is convenient to do using list.

Recursion versus tail recursion
It is worth watching carefully what happens (Table 21):  We can remove one parameter in such a way (Table 22):  The base case could be the same in Horner1 and Horner1p, of course.I leave it up to the reader to check how they work.6. Iterative version and testing its execution (Table 23).
The first proposition: to Hornerit :n :x :a :w repeat :n [let "w :w * :x + first :a let "a bf :a] op :w end ?show Hornerit 4 -2 [0 -4 4 -5] 3 19 Table 23 variables loop condition: We can conclude that this procedure is executed exactly like the tail recursive one.
The second proposition (Table 24): to Hornerit :x :a let "w first :a let "a bf :a repeat count :a [let "w :w * :x + item repc :a] op :w end ?show Hornerit -2 [3 0 -4 4 -5] 19 Table 24 variables loop condition: The third proposition (Table 25): to Hornerit :x :a let "w first :a let "a bf :a while [count :a <> 0] [let "w :w * :x + first :a let "a bf :a] op :w end Now I come back to the question asked at the end of the previous section.Verification whether the algorithm works correctly could require conversion of the binomial representation of a given number into the decimal one.This is equivalent to the evaluation the polynomial defined by the coefficients being the binary representation at x equal to 2. For our example we have:

Now we can check:
? show Horner1p 2 [1 1 0 0] 0 12 or more compact, with the procedural parameter decbinp 12 [ ] (a parameter of a procedure that is itself a procedure): It is not a proper mathematical proof of the algorithm correctness, but it is sufficient for the beginners.
In Appendix 2 two other examples with the list as a data structure and the more interesting student's results (Tables 28, 29) are presented.

Conclusions
A method has been proposed to compare and explain the relations between recursive and iterative versions of the same algorithm and illustrated by a few basic examples.Such a training convinces that we really understand what does it mean that every algorithm has its recursive and iterative version and we can do the transformation from one version to the other.In this paper I decided to present the sequence of steps in the direction from the recursion to the iteration, although sometimes when preparing it I started with the iteration version, which is another argument for the correct performance of the method.The method shown in the paper has his own restriction: it is working well for the functions with the one recursion call in the body of the procedure.All examples discussed in the paper are the functions of discrete mathematics or can be used for integers.These examples I have used to compose different questions to test the knowledge and understanding of the students.The reader can use my examples to construct a lot of fixed-code and skeleton-code questions (McCartney et al., 2005) for students.I was working with my students with this method only for one year.In one year time I will probably have more interesting results on its performance and may inform you about it.I will be grateful for any comments.I. Foltynowicz is a senior lecturer at the Theoretical Chemistry Department, Faculty of Chemistry, Adam Mickiewicz University, Poznań, Poland.She has many years of experience in teaching quantum chemistry, numerical methods, statistics and theory of probability as well as algorithms and data structures and basic concepts of programming (not so many years in the latter).She has also several years of experience in teaching informatics at a secondary school (it is a rather closed chapter in her live, but who knows).She received her PhD in theoretical chemistry (more exactly theoretical spectroscopy) from the A. Mickiewicz University in Poznań.She is the author of one academic script, one book and a few scientific papers.She is a fan of Logo (first AC-Logo, then Comenius Logo and now Imagine Logo) as an educational tool mainly for recursion and fractal geometry.

Fig. 1 .
Fig. 1.Flow charts of recursive and tail recursive versions of the factorial function.

Fig. 2 .
Fig. 2. Flow charts of iterative and tail recursive versions of the factorial function.

Fig. 3 .
Fig. 3. Plot of the function z = x div y made in Excel.

Fig. 4 .
Fig. 4. Plot of the function z = x mod y made in Excel.

Fig. 5 .
Fig. 5. Plot of the function z = x div y made in Sigma-Plot.

Fig. 6 .
Fig. 6.Plot of the function z = x mod y made in Sigma-Plot.