2

I am trying to write a function using Common Lisp functions only that will count how many s-expressions are in an s-expression. For example:

((x = y)(z = 1)) ;; returns 2

and

((x - y)) ;; returns 1

Nested expressions are possible so:

((if x then (x = y)(z = w))) ;; returns 3

I wrote a function which finds the length and it works if no nested expressions are there. It is:

(define (length exp)
  (cond
    ((null? exp) 0)
    (#t (+ 1 (length (cdr exp))))))

Now I modified this in an attempt to support nested expressions to the following:

(define (length exp)
  (cond
    ((null? exp) 0)
    ((list? (car exp)) (+ 1 (length (cdr exp))))
    (#t (length (cdr exp)))))   

This works for expressions with no nests, but is always 1 less than the answer for nested expressions. This is because taking the example above, ((if x then (x = y)(z = w))), this will look at if at first and which satisfies the third condition, returning the cdr (the rest of the expression as a list) into length. The same happens up until (x=y) is reached, at which point a +1 is returned. This means that the expression (if x then .... ) has not been counted.

In what ways would I be able to account for it? Adding +2 will over-count un-nested expressions.

I will need this to work in one function as nesting can happen anywhere, so:

((x = y) (if y then (z = w)))
darksky
  • 20,411
  • 61
  • 165
  • 254
  • 1
    This looks strange. You say 'I am trying to write a function using Common Lisp functions only'. Yet, your code is in Scheme? – Rainer Joswig Nov 28 '12 at 06:17
  • @RainerJoswig my code is in scheme but I'm only using LISP numeric primitives, predicates and functions. – darksky Nov 28 '12 at 15:13
  • okay, but this has nothing to do with Common Lisp. Common Lisp has a lot of functions - more than an educational Scheme. – Rainer Joswig Nov 28 '12 at 15:46
  • @RainerJoswig Alright - I really don't know the difference, I just got started with Scheme. – darksky Nov 28 '12 at 19:10

1 Answers1

2

At first sight, your code only recurses to the right (cdr-side) and not to the left (car-side), so that definitely is a problem there.

On second sight, this is even a little bit more tricky than that, because you are not exactly counting conses; you need to differentiate the case where a cons starts a proper list vs where it's the cdr of a list. If you were to recurse to the car and cdr, that information would be lost. We need to iterate over the sexp as a proper list,

(defun count-proper-list (sexp)
  (cond ((atom sexp) 0)
        (t (1+ (reduce #'+ (mapcar #'count-proper-list sexp))))))

But this will also count the top level list, therefor always return one more than what you seem to want. So perhaps,

(defun count-proper-sublist (sexp)
  (1- (count-proper-list sexp)))
huaiyuan
  • 26,129
  • 5
  • 57
  • 63
  • Thanks. I'm not sure what mapcar or reduce do, but I'm not allowed to use them for this. I see where my problem is. It's that I'm missing some lists or elements in my recursion. Speaking conceptually, I need to walk the sexp as a proper list. 1- How is that done? 2- How would I still detect nested sexps? Sorry I didn't get it all. – darksky Nov 28 '12 at 15:28