计算LISP中每个级别的子列表

时间:2014-10-29 10:43:52

标签: list lisp common-lisp sbcl

我需要编写一个程序来计算每个级别的子列表并将其打印为

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

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

因此,对于(A (B (C)) D),它将是( (1 1) (2 1) (3 1) )

我写过这个程序。逻辑是我每次削减头部,检查,如果它是一个列表,我增加计数器,然后将列表的其余部分附加到我的other部分。否则,只需切断并继续尾巴。

我正在使用SBCL并收到此错误:

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

这是我的代码:

(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)))

2 个答案:

答案 0 :(得分:2)

调试你已经拥有的东西

这是您的代码,有些更惯用的格式:

(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 ())))

您可以通过更简单的测试获得相同的错误消息:

(count-sublists '(a))

更简单的测试可以更容易地推断将要发生的事情。

  1. 检查'(a)是否为空。不是,所以
  2. 您获得myhead = amytail = '()
  3. 检查a是否为列表。它不是那么
  4. 执行(iter mytail (append myhead other) ...),但
  5. append将列表作为参数,您已经知道myhead不是列表。
  6. 改为使用(list* myhead other)(cons myhead other)(append (list myhead) other)

    使用系统的调试器

    错误消息使您看起来可能正在使用SBCL,在这种情况下,您可以使用优化编写代码进行调试,然后回溯将更有帮助。首先,将代码更改为

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

    然后,当您运行测试并获得错误时,您可以键入BACKTRACE并获取指向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))
    ; ...
    

    更惯用的解决方案

    要想出一个特别优雅的解决方案,这不是一个简单的问题,但这是一种合理有效的方法。它创建了一个哈希表,将深度映射到出现次数(即,它本质上是一个直方图)。

    (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)) 
    

答案 1 :(得分: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)))