[net.applic] recursion and efficiency and krc

srlm@ukc.UUCP (S.R.L.Meira) (08/16/83)

     This article is about recursion (and factorial!) again, in relation
to   Joe  Fasel's.   The  examples  are  in  KRC  --The  Kent  Recursive
Calculator-- parts of which  have  been  shown  in  a  previous  article
(RECURSION IS EASIER...).

     The "usual" definition of factorial

    fac1 0 = 1
    fac1 n = n * fac1 (n-1)

is not only the simplest way to demonstrate recursion, but it  is  prob-
ably  the  most  efficient  way  for  the calculation of factorial (in a
purely applicative language).  The second method shown in Joe's  article
uses the "lazy list" concept, i.e., using the notation

    lis n = [1..n]

one defines the list

    [1,2,3...n]

which is built by need.  The in(de)crement is not  necessarily  1,  such
that

    odd = [1,3..]

is the infinite list of odd integers.  And so on.  But  when  we  define
factorial as

    fac2 n = product [1..n]

its  evaluation  uses  much  more  space  than  fac1's  above.   In  the
ICL2960/EMAS   (tree-rewriting-system)  implementation  of  KRC  (by  D.
Turner) fac2 uses 60% more space than fac1 and in the experimental  Unix
(combinators)  implementation (by S. Croft) it uses 200% more space, the
number  of  reductions  needed  for  both  evaluations  being  basically
unchanged.

     I agree with Joe (and David Turner) that the use of product, a  KRC
library function defined by

    product = fold '*' 1
    fold op s [] = s
    fold op s (a:x) = op a (fold op s x)

eliminates the "junk recursion" in fac1, but it has its price.

     By the way, the use of tail recursion

    fac3 n = fac3t n 1
    fac3t 0 r = r
    fac3t n r = fac3t (n-1) (n*r)

is also less efficient in the two  implementations of  KRC here at  UKC.
fac3  evaluates  with the same number of reductions of fac1, but it uses
20% more space in the tree-rew-sys and  50%  more  in  the  combinatoric
machine.  So...

     Now I'll say a little about  eliminating  recursion  by  using  ZF-
expressions (see my previous article).  A silly example of its use is to
redefine the length function

    length [] = 0
    length (a:x) = 1 + length x

whose ZF version is

    lenZF x = sum {1 | a <- x}

where {1 | a <- x} is a list containing as many 1's  as  the  number  of
elements in x, and sum is

    sum = fold '+' 0

A favourite example is the infinite list of primes

    primes = sieve [2..]
    sieve (a:x) = a : sieve {b | b <- x; b % a = 0}

where % is "remainder" and \= is "not equal".

     From now on, we can redefine

    map f [] = []
    map f (a:x) = f a : map f x

as

    map f x = {f a | a <-x}

and

    filter cond [] = []
    filter cond (a:x) = a : filter cond x, cond x
                      = filter cond x

as

    filter cond x = {a | a <- x; cond a}

and so on.

     The only alert here is that both "product" and  ZF-expressions  are
(internally) recursively defined.




                           Silvio Lemos Meira
			   ...!decvax!mcvax!ukc!srlm