Imagine that you already have your function. What does he get? What should he produce?
Given an atom, what does it return? Given a simple list of atoms, what should it return?
(defun condense (x) (typecase x (number ; then what? (condense-number x)) (list ; then what? (if (all-atoms x) (condense-list-of-atoms x) ; how to do that? (process-further-somehow (condense-lists-inside x)))) ; what other clauses, if any, must be here? ))
What do condense-lists-inside need to do? According to your description, it should condense nested lists inside - each into a number, and leave the atoms intact. Therefore, he will leave a list of numbers. To somehow handle this, we already “have” a function, condense-list-of-atoms , right?
Now how to implement condense-lists-inside ? It's easy
(defun condense-lists-inside (xs) (mapcar
What? Why, condense , of course! Remember, we assume that we already have this. As long as he receives what he wants to receive, he must produce what he intended for production. Namely, given an atom or list (with possible nested lists inside), it will produce a number.
So now fill in the blanks and simplify. In particular, see if you really need an all-atoms check.
edit: using typecase was a bad choice as it treats NIL as a LIST. We need to look at NIL differently to return a "null value" instead. Therefore, it is better to use the usual construction (cond ((null x) ...) ((numberp x) ...) ((listp x) ...) ... ) .
About your new code: you were mistaken: to process the list of atoms returned after (mapcar #'condense x) , we have a calculate function that does this, we don’t need to go back so far to condense . When you replace calculate there, it will become obvious that checking for all-atoms is not needed at all-atoms ; it was just a pedagogical device to facilitate code development. :) It’s normal to make extra choices when we develop, if we then simplify them, then we have reached the goal of correctness !
But removing the all-atoms check will violate your # 2 requirement. Then the calculation will act as follows:
(CONDENSE '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5)) == (calculate (mapcar #'condense '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5))) == (calculate (list 2 3 4 (condense '(3 1 1 1)) (condense '(2 3 (1 2))) 5)) == (calculate (list 2 3 4 (calculate '(3 1 1 1)) (calculate (list 2 3 (calculate '(1 2)))) 5)) == (calculate (list 2 3 4 6 (calculate '(2 3 3)) 5)) == (calculate (list 2 3 4 6 8 5)) == 28
those. he will act from left to right, and not from the deepest nested level. Representing the nested list as a tree (this is it), it “chews” on the tree from its deepest left corner up and to the right; code with the all-atoms tag will be executed strictly upward.
Thus, the final simplified code:
(defun condense (x) (if (listp x) (reduce
note:. Looking at the last illustration of the recovery sequence, a clear image appears - replacing each node in the argument tree with a calculation application. This is an obvious case of folding , just one that runs on the tree instead of a simple list, since reduce is.
This can be directly encoded using the so-called "car-cdr recursion", replacing each cons cell with an application of the union function f with two results of recursive calls to the car and cdr components of the cell:
(defun condense (x) (reduce-tree x
As you can see, this version is very recursive, which is not very good.