简体   繁体   中英

How can I make my average function tail recursive in Lisp

I am simply trying to make this average function to be tail recursive. I have managed to get my function to work and that took some considerable effort. Afterwards I went to ask my professor if my work was satisfactory and he informed me that

  1. my avg function was not tail recursive
  2. avg did not produce the correct output for lists with more than one element

I have been playing around with this code for the past 2 hours and have hit a bit of a wall. Can anyone help me to identify what I am not understanding here.

Spoke to my professor he was != helpful

    (defun avg (aList)
        (defun sumup (aList)
            (if (equal aList nil) 0
                ; if aList equals nil nothing to sum
                (+ (car aList) (sumup (cdr aList)) )
            )
        )

        (if 
            (equal aList nil) 0
            ; if aList equals nil length dosent matter
            (/ (sumup aList) (list-length aList) )
        )
    )

    (print (avg '(2 4 6 8 19))) ;39/5

my expected results for my test are commented right after it 39/5

So this is what I have now

    (defun avg (aList &optional (sum 0) (length 0))

            (if aList 
            (avg (cdr aList) (+ sum (car aList))
            (+ length 1)) 
            (/ sum length)))

    (print (avg '(2 4 6 8 19))) ;39/5
(defun avg (list &optional (sum 0) (n 0))
   (cond ((null list) (/ sum n))
         (t (avg (cdr list) 
                 (+ sum (car list)) 
                 (+ 1 n)))))

which is the same like:

(defun avg (list &optional (sum 0) (n 0))
   (if (null list) 
       (/ sum n)
       (avg (cdr list) 
            (+ sum (car list)) 
            (+ 1 n))))

or more similar for your writing:

(defun avg (list &optional (sum 0) (n 0))
   (if list
       (avg (cdr list) 
            (+ sum (car list)) 
            (+ 1 n))
       (/ sum n)))
(defun avg (lst &optional (sum 0) (len 0))
   (if (null lst)
       (/ sum len)
       (avg (cdr lst) (incf sum (car lst)) (1+ len))))

You could improve your indentation here by putting the entire if-then/if-else statement on the same line, because in your code when you call the avg function recursively the indentation bleeds into the next line. In the first function you could say that if the list if null (which is the base case of the recursive function) you can divide the sum by the length of the list. If it is not null, you can obviously pass the cdr of the list, the sum so far by incrementing it by the car of the list, and then increment the length of the list by one. Normally it would not be wise to use the incf or 1+ functions because they are destructive, but in this case they will only have a localized effect because they only impact the optional sum and len parameters for this particular function, and not the structure of the original list (or else I would have passed a copy of the list).

Another option would be to use a recursive local function, and avoid the optional parameters and not have to compute the length of the list on each recursive call. In your original code it looks like you were attempting to use a local function within the context of your avg function, but you should use the "labels" Special operator to do that, and not "defun":

(defun avg (lst)
   (if (null lst)
       0
       (labels ((find-avg (lst sum len)
          (if (null lst)
              (/ sum len)
              (find-avg (cdr lst) (incf sum (car lst)) len))))
          (find-avg lst 0 (length lst))))

I'm not 100% sure if your professor would want the local function to be tail-recursive or if he was referring to the global function (avg), but that is how you could also make the local function tail-recursive if that is an acceptable remedy as well. It's actually more efficient in some ways, although it requires more lines of code. In this case a lambda expression could also work, BUT since they do not have a name tail-recursion is not possibly, which makes the labels Special operator is useful for local functions if tail-recursion is mandatory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM