【问题标题】:Counter variable in LISPLISP 中的计数器变量
【发布时间】:2014-11-02 22:31:46
【问题描述】:

定义函数 'occ',它接受一个列表 L 和一个符号 A,并计算符号 A 在 L 中的出现次数。 例子: (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 的任何高级功能。

【问题讨论】:

  • 一些事情:您在每次通话时都将计数器设置为 0。你没有返回一个值。如果第一个调用返回 true,'or' 不会调用第二个参数。

标签: math lisp common-lisp


【解决方案1】:

首先,不要使用setq在yout函数内部进行变量初始化,使用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

【讨论】:

  • or 不是谓词 - 它返回 nil 或任何对象,因此它仍然可以与 setf 一起使用以分配非布尔值。
【解决方案2】:

如果您将定义提供给 SBCL:

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

所以你正在修改一个全局未定义变量counter。函数什么时候返回?好吧,或者将使用carcdr 从递归返回第一个非nil。什么返回值?好吧,当它不是一个缺点时,它会在符号匹配时评估为计数器的 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)))))

counter 是一个可选的累加器,用于保存值。由于它已传递,因此它不会在递归调用之间共享,而是在每次调用时替换为更新的值,使其具有功能性且易于遵循。当您需要同时搜索carcdr 时,您使用此阶段的计数器递归car,返回值将用作cdr 中的计数器。对于原子列表,如果实现支持它,这将是尾递归的。这支持将符号查找为列表的尾部。例如。 (occ '((x . x) . x) 'x) ; ==> 3 如果您确定没有点列表(每个列表都以 nil 结尾),您可以使用 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". 

【讨论】:

  • 对同一个变量求和和计数不是一个好主意:任何两个不累积相同类型对象的子句只有在每个子句将其值累积到一个不同的变量。在这种情况下 fixnumnumber.
  • @RainerJoswig 已修复 :-)(我认为这可能不是一个好主意)
猜你喜欢
  • 1970-01-01
  • 2015-11-13
  • 2010-11-18
  • 1970-01-01
  • 2019-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-17
相关资源
最近更新 更多