0

I need to write a program which counts sublists on each level and prints it like

((1 2) (2 1) (3 1))

( (<level> <count>) (<level> <count> ... )

So, for (A (B (C)) D) it will be ( (1 1) (2 1) (3 1) )

I have wrote this program. The logics is that I each time cut head, check, if it is a list, I increment counter, then append the rest of the list to my other part. Otherwise, just cut and continue with tail.

I'm using SBCL and getting this error:

 debugger invoked on a TYPE-ERROR in thread
 #<THREAD "main thread" RUNNING {1002C0EB73}>:
 The value A is not of type LIST.

Here's my code:

(defun count-sublists (list)
  (labels
    ((iter (current other count level res)
      (if (null current)
        (if (null other)
          (cons (list level count) res)
          (iter other () 0 (1+ level) (cons (list level count) res)))
      (let ((myhead (car current)) (mytail (cdr current)))
         (if (listp myhead)
           (iter mytail other (1+ count) level res)
           (iter mytail (append myhead other) count level res))))))
      (iter list () 0 1 ())))

 (print (count-sublists '(A (B) C)))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
vladfau
  • 1,003
  • 11
  • 22

2 Answers2

2

Debugging what you've got

Here's your code, with somewhat more idiomatic formatting:

(defun count-sublists (list)
  (labels ((iter (current other count level res)
             (if (null current)
                 (if (null other)
                     (cons (list level count) res)
                     (iter other () 0 (1+ level) (cons (list level count) res)))
                 (let ((myhead (car current))
                       (mytail (cdr current)))
                   (if (listp myhead)
                       (iter mytail other (1+ count) level res)
                       (iter mytail (append myhead other) count level res))))))
    (iter list () 0 1 ())))

You can get the same error message with a simpler test:

(count-sublists '(a))

A simpler test makes it easier to reason about what will happen.

  1. Check whether '(a) is null. It's not, so
  2. you get myhead = a, and mytail = '().
  3. Check whether a is a list. It's not so
  4. Do (iter mytail (append myhead other) ...), but
  5. append takes lists as arguments, and you already know that myhead isn't a list.

Use (list* myhead other) or (cons myhead other) or (append (list myhead) other) instead.

Using the system's debugger

The error message makes it look like you might be using SBCL, in which case you could write your code with an optimization for debugging, and then backtraces will be more helpful. First, change your code to

(defun count-sublists (list)
  (declare (optimize debug))
  ; ...

Then, when you run the test and get the error, you can type BACKTRACE and get information that will point you to append:

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {1002FDE853}>:
  The value A is not of type LIST.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [RETRY   ] Retry EVAL of current toplevel form.
  1: [CONTINUE] Ignore error and continue loading file "/home/taylorj/tmp/count-levels.lisp".
  2: [ABORT   ] Abort loading file "/home/taylorj/tmp/count-levels.lisp".
  3:            Exit debugger, returning to top level.

(SB-IMPL::APPEND2 #<unavailable argument> #<unavailable argument>) [tl,external]
0] BACKTRACE

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {1002FDE853}>
0: (SB-IMPL::APPEND2 #<unavailable argument> #<unavailable argument>) [tl,external]
1: ((LABELS ITER :IN COUNT-SUBLISTS) (A) NIL 0 1 NIL)
2: (COUNT-SUBLISTS (A))
; ...

A more idiomatic solution

This isn't an easy problem to come up with a particularly elegant solution for, but here's one approach that's reasonably efficient. It creates a hash table that maps depths to number of occurrences (i.e., it's essentially a histogram).

(defun count-sublists (object &aux (table (make-hash-table)) (max 0))
  (labels ((incf-depth (depth)
             (setf max (max max depth))
             (incf (gethash depth table 0)))
           (map-depth (object depth)
             (when (listp object)
               (incf-depth depth)
               (dolist (x object)
                 (map-depth x (1+ depth))))))
    (map-depth object 1)
    (loop for depth from 1 to max
       collecting (list depth (gethash depth table 0)))))

(count-sublists '(A (B) C))
;=> ((1 2) (2 1))

(count-sublists '(A (B (C)) D))
;=> ((1 2) (2 1) (3 1))

(count-sublists '(a (((b c))) d))
;=> ((1 1) (2 1) (3 1) (4 1)) 
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
0
(defun count-sublists (list)
  (labels ((iter (current other count level res)
         (if (null current)
             (if (null other)
                 (reverse (cons (list level count) res))
                 (iter other () 0 (1+ level) (cons (list level count) res)))
             (let ((myhead (car current))
                   (mytail (cdr current)))
               (if (listp myhead)
                   (iter mytail (append myhead other) (1+ count) level res)
                   (iter mytail other count level res))))))
(iter list () 0 1 ())))

(print (count-sublists '(A (B) C)))
Sevak.Avet
  • 111
  • 1
  • 10