LISP中的计数器变量

时间:2014-11-02 22:31:46

标签: math lisp common-lisp

定义功能' occ'采用列表L和符号A并计算L中符号A的出现。 例: (occ'(((s)o)d)' f) - > 0

到目前为止我得到了什么:

(defun occ(list a)
(setq counter 0)
  ;Checks if the given list is has an nested list
  (if (consp list)
    ; Breaking the list down atom by atom and recursing
    (or (occ a (car list))
        (occ a (cdr list)))
    ; checks if symbols are the same
    (if(eq a list)
        (setq counter(1+ counter)))))

但是我的输出继续说Nil而不是显示计数器值。 我不能使用任何更高级别的LISP功能。

2 个答案:

答案 0 :(得分:6)

首先,不要在你的函数中使用setq进行变量初始化,使用let。其次,让我们看看为什么你做错了,你的代码:

(defun occ(list a)
(setq counter 0) ;; You always setting counter to 0 on new
                 ;; level of recursion
  (if (consp list)
   (or (occ a (car list))  ;; You reversed arguments order?
        (occ a (cdr list))) ;; according to your definition it must be
                            ;; (occ (car list) a)
    (if(eq a list)
        (setq counter(1+ counter)))))

无论如何,你不需要任何计数器变量来做你想做的事。

正确的函数可能看起来像这样(我更改了参数顺序,因为我在LIST中找到SYMBOL看起来更好):

(defun occ (sym nested-list)
  (cond
    ((consp nested-list)
     (+ (occ sym (car nested-list)) (occ sym (cdr nested-list))))
    ((eq sym nested-list) 1)
    (t 0)))

CL-USER> (occ 'x '(((s) o ((f ()) f)) d))
0

CL-USER> (occ 'f '(((s) o ((f (x (((f))))) f)) d f))
4

答案 1 :(得分:1)

如果您将定义提供给SBCL:

; in: DEFUN OCC
;     (SETQ COUNTER 0)
; 
; caught WARNING:
;   undefined variable: COUNTER
; 
; compilation unit finished
;   Undefined variable:
;     COUNTER
;   caught 1 WARNING condition

因此,您正在修改全局未定义变量counter。该功能何时返回?好吧,或者会使用nilcar返回递归的第一次非cdr返回。什么返回值?当它不是缺点时,它会在符号匹配时评估为incf计数器的中间值,或者当它不匹配时评估为nil

尝试这样做:

(defun occ (list a &optional (counter 0))
  (cond ((equal list a) (1+ counter))
        ((atom list) counter)
        (t (occ (cdr list)
                a
                (occ (car list)
                   a
                   counter)))))

计数器是一个可选的累加器,用于保存值。由于它已经通过,因此它不会在递归调用之间共享,而是在每次调用时替换为更新后的值,使其功能强大且易于遵循。当您需要同时搜索carcdr时,您使用此阶段的计数器递归car,并且返回值将用作cdr中的计数器。对于atom的列表,如果实现支持它,那么它将是尾递归的。这支持将符号作为列表的尾部进行查找。例如。 (occ '((x . x) . x) 'x) ; ==> 3如果您确定没有点列表(每个列表都没有终止),您可以使用loop宏:

(defun occ (list a)
  (loop :for e :in list 
        :counting (equal e a) :into count
        :if (consp e)
            :summing (occ e a) :into sum
        :finally  (return (+ count sum))))

;; tests
(occ '(x (x x (x (x ) x)) y z) 'y) ; ==> 1
(occ '(x (x x (x (x ) x)) y z) 'x) ; ==> 6
(occ '((x . x) . x) 'x) ; ERROR like "A proper list must not end with X".