Search code examples
listlispcommon-lispnested-listsdo-loops

Common Lisp function to determine lengths of nested lists in a list


I am only allowed to use the do construct, concept which i still can't get a grasp of. I tried the following code, but it returns nil.

(defun fun(list)
(do ((i 0 (+ i 1)) (l '() (if (listp (nth i list)) (append l (list (length (nth i list)))))))
((null (nth i list)) l)
)
)

Is the updated value of l wrong? The output for this list (a (b) (c d)) should be (1 2).


Solution

  • Try to think in terms of lists instead of arrays. You are using the n-th element of a list as if it was an array. Instead, with do you can traverse the list by taking the next sublist each time, i.e. dropping the first element and getting another list without that first element.

    (defun fun(list)
      (do ((l list (cdr l))
           (result nil) )
          ((null l) (nreverse result))
        (if (listp (car l))
          (push (length (car l)) result) )))
    

    The do operator takes three arguments: the list of variables, the end condition and the body of the loop. The first includes the name of the variables, their initial value and (optionally) how they change from one loop to the next. For example, (l list (cdr l)) says that you use a variable l whose initial value is the input list and from one loop to the next it will become the cdr of itself, i.e. it will lose its first element. The ending condition includes also the return value of the function. With ((null l) (nreverse result)) we are saying that when the variable l is null, the function will end and return the value (nreverse result). Why nreverse? Because we use push in the body, which accumulates values in the wrong order. Finally, the body tells the function to add to result the length of the first element of l whenever this is a list.