在公共lisp中编写一个函数,它将返回列表的否定

时间:2010-12-14 02:33:33

标签: lisp common-lisp

我使用当前代码收到此错误:

 LET: illegal variable specification
   (COND (LISTP A (IF (NEGATE A) (NEGATE (REST L)) NIL))
    (T (SETF A (-A) (APPEND (LIST A) (REST L)) (NEGATE (REST L)) NIL)))

我目前的代码:

(defun negate(L)
 (setq x -1)

 (if (not (null L))

  (let ((a (fitst L))
        (cond (listp a
              (if (negate a)
                  (negate (rest L))
                nil))
        (t
         (setf a (-a) (append (list a)(rest L))
               (negate (rest L))
               nil))))
)
))

以及需要传递的测试用例

o List is  (1 2 3 4)  
o Output should be: (-1 -2 -3 -4)

o List is  (1 -2 (3 4))  
o Output should be: (-1 2 (-3 -4) )

5 个答案:

答案 0 :(得分:4)

在最礼貌的意义上,你的代码有点偏。你这周学习Lisp,不是吗?没关系!这是一种有趣的语言,可以做一些很棒的事情。

因此,我将逐步完成例行程序的制作,并带您一起参观。

您的基本情况是 -

(defun negate (n) 
  (if (> n 0) (- 0 n)))

(map #'negate '(1 2 3 4))

走树是更复杂的,但让我们来看看这些想法。

基本上,你有三种情况要回答:当前元素是nil,列表还是原子?

(if (not (car seq)) 
  (if (listp (car seq))
    ;;Recurse
    ;;Otherwise negate the current element and append it to the recursed.

让我们先尝试一下:

(defun negate-seq (seq)
  (if (not seq)
      (return-from negate-seq))

  (if (listp (car seq))
      (negate-seq seq)
    (list (negate (car seq)) (negate-seq (cdr seq)))))

太棒了! 除了...

(negate-seq '(1 2)) ==> (-1 (-2 NIL))

和...

 (negate-seq '(1 (1 2 -3))) ==> STACK OVERFLOW!

哦,小伙子。我们现在遇到了麻烦。

首先,让我们尝试cons而不是list。 这清除了奇怪的嵌套列表问题。

很明显,我们已经进入了无限递归的循环。这应该是不可能的,因为我们有not seq后卫。好的,让我们试试调试吧。我正在使用CLISP,我可以跟踪:

(trace 'negate-seq) 

然后,

(negate-seq '(1 (1 2 -3)))

突然间,我看到了

的爆炸声
1621. Trace: (NEGATE-SEQ '((1 2 -3)))
1622. Trace: (NEGATE-SEQ '((1 2 -3)))
1623. Trace: (NEGATE-SEQ '((1 2 -3)))
1624. Trace: (NEGATE-SEQ '((1 2 -3)))

Crikey,我忘记了我的cdr并且列出了这个名单! Hmmmm。

我们试试这个:

(defun negate-seq (seq)
  (if (not seq)
      (return-from negate-seq))

  (if (listp (car seq))
      (cons (negate-seq (car seq))
        (negate-seq (cdr seq)))
    (cons (negate (car seq)) (negate-seq (cdr seq)))))

回收汽车,回避汽车,将它们放在一起,我们可能会有所作为。

 (negate-seq '(1 (1 2 -3))) =>  (-1 (-1 -2 NIL)

Hmmmm。我们来看看跟踪。

  
      
  1. 追踪:(NEGATE-SEQ'(1(1 2 -3)))
  2.   
  3. 追踪:(NEGATE-SEQ'((1 2 -3)))
  4.   
  5. 追踪:(NEGATE-SEQ'(1 2 -3))
  6.   
  7. 追踪:(NEGATE-SEQ'(2 -3))
  8.   
  9. 追踪:(NEGATE-SEQ'( - 3))
  10.   
  11. 追踪:(NEGATE-SEQ'NIL)
  12.   
  13. 追踪:NEGATE-SEQ ==> NIL
  14.   
  15. 追踪:NEGATE-SEQ ==> (NIL)
  16.   
  17. 追踪:NEGATE-SEQ ==> (-2 NIL)
  18.   
  19. 追踪:NEGATE-SEQ ==> (-1 -2 NIL)
  20.   
  21. 追踪:(NEGATE-SEQ'NIL)
  22.   
  23. 追踪:NEGATE-SEQ ==> NIL
  24.   
  25. 追踪:NEGATE-SEQ ==> ((-1 -2 NIL))
  26.   
  27. 追踪:NEGATE-SEQ ==> (-1(-1 -2 NIL))
  28.   

所以我递归到-3,然后......它会掉下来?奇。啊!我不断抓住事情的CDR。 CDR始终是列表。 (cdr'( - 3))是零!

让我们看看......

(大概翻找)

否定在正面回报为零。 D'哦。

(defun negate (n) 
  (if ( > n 0) 
      (- 0 n)
    n))


(defun negate-seq (seq)
  "Written by Paul Nathan"
  (if (not seq)
      (return-from negate-seq))

  (if (listp (car seq))
      (cons (negate-seq (car seq))
        (negate-seq (cdr seq)))
    (cons (negate (car seq)) 
      (negate-seq (cdr seq)))))

答案 1 :(得分:3)

我不确定您是否正在寻找一个温和的推动来纠正所提供的代码,或者您是否正在寻求其他方式来实现它。我的第一个念头是mapcar

(defun negate-tree (tree)
  (mapcar (lambda (e)
            (cond 
              ((null e) nil)
              ((listp e) (negate-tree e))
              (t (- e))))
          tree))

然后你可以概括出否定方面,然后编写map-tree,接受一个函数应用于树中的原子:

(defun map-tree (f tree)
  (mapcar (lambda (e)
            (cond 
              ((null e) nil)
              ((listp e) (map-tree f e))
              (t (funcall f e))))
          tree))

你可以用一元否定函数来调用它:

(map-tree #'- '(1 -2 (3 4)))

这样的调用假定树中的所有叶子都是nil由一元否定函数容纳的。

接受nil作为树中可能的叶子使得访问算法有点混乱,并且不清楚所提供的函数f是否应该应用于所有叶子 - 甚至那些nil 1}} - 以便函数本身可以决定是否以及如何处理nil

此版本的另一个缺点是它如何处理非proper lists的cons细胞。请注意,函数listp对于所有cons单元都返回true - 即使那些不构成正确列表的那些 - 但mapcar确实要求其输入是正确的列表。我们可以沿着“listp true”路径结束,递归调用mapcar,并且mapcar无法接收不正确的列表。这意味着上面的算法要么需要测试cons单元格,看看它们是否是正确的列表,然后再将它们交给mapcar,也许处理那些不是叶子的那些(我不愿意说“原子”)这里记录,或者记录预期的树结构由适当的列表组成。

如果你需要接受顶级“树”,这些树不一定是列表本身,意味着一个孤立的原子是一个有效的树,或者nil是一个有效的树,你可以撕掉它们的组成部分。在确定被检查的树是列表之后,在上面的函数中编写一个仅使用mapcar的函数。

答案 2 :(得分:3)

如果你写:

 (defun negate (n)
   (if ( > n 0)
     (- 0 n)
     n))

然后你将你的代码限制为实数。

如果您使用Common Lisp提供的原始否定函数,它将适用于任何数字:

(mapcar (function -) '(1 2/3 #C(4 5)))     
--> (-1 -2/3 #C(-4 -5))

答案 3 :(得分:0)

这就是我要做的。当然,我只考虑数字列表。因此,如果列表不是全数字,它会抛出错误。

(defun negate (list)
      (flet ((negate-number (x)
           (- x)))
    (labels ((negate-helper (list neg-list)
           (if (null list)
               neg-list ; when all elements are considered return neg-list
               (let ((num-or-list (car list)))
             (if (numberp num-or-list)
                 ;; if number then negate it and add it into the new list (i.e. neg-list)
                 (negate-helper (cdr list) (append neg-list (list (negate-number num-or-list))))
                 ;; if list then first negate the sublist
                 (negate-helper (cdr list) (append neg-list (list (negate-helper num-or-list nil)))))))))
      (negate-helper list nil))))

答案 4 :(得分:0)

您可以通过以下步骤实现此目的:

(defun negate (l)"returns a list of multiplication negative of elements of a list l,
                  element of list l to be each numbers for not to type err,
                  and list l may not be a list of atoms."
  (cond
   ((null l) nil)
   ((consp (car l)) (cons (negate (car l))
                          (negate (cdr l))))
   (t (cons (* -1 (car l))
            (negate (cdr l))))))

还可以有另一个版本。我试图写一个尾递归程序,但它不完全是tr。

实现同样的目的,除了它没有给出与原始顺序相同的顺序。我现在无法解决这个问题:

(defun negate (l)
  (negate-aux l '()))

(defun negate-aux (l A)
  (cond
   ((null l) (reverse A));or A
   ((consp (car l)) (cons (negate (car l))
                          (negate-aux (cdr l) A)))
   (t (negate-aux (cdr l) (cons (* -1 (car l)) A)))))