稳定联盟

时间:2012-10-11 20:15:32

标签: lisp

需要在lisp中编写一个union函数,它将两个列表作为参数,并返回一个列表,该列表是两者的并集,没有重复。订单应与输入列表的顺序一致

例如:如果输入是'(a b c)和'(e c d),结果应为'(a b c e d)

这是我到目前为止所拥有的

(defun stable-union (x y)
  (cond
   ((null x) y)
   ((null y) x))
  (do ((i y (cdr i))
       (lst3 x (append lst3 
                       (cond
                        ((listp i) 
                         ((null (member (car i) lst3)) (cons (car i) nil) nil))
                        (t (null (member i lst3)) (cons i nil) nil)))))
        ((null (cdr i)) lst3)))

我的错误是有一个"非法的功能对象"与段(null(member(car i)lst3))

么?

4 个答案:

答案 0 :(得分:1)

错误是因为您正在尝试执行评估(null (member (car i) lst3))的结果。在 cond 表达式中,如果 i 是一个列表,那么它会尝试评估表达式

((null (member (car i) lst3)) (cons (car i) nil) nil))

并返回结果。表达式中的第一个元素应该是一个函数,但是

(null (member (car i) lst3))

将返回一个布尔值。因此失败了。您的代码结构需要注意。你错过的是你需要一个内在的 cond

顺便说一句,如果你以递归的方式做这件事,这将是一个更清晰的功能。

我是Schemer而不是Lisper,但我有点想一想。这是递归实现的框架:

(defun stable-union (x y)
  (cond
    ((null x) y)
    ((null y) x)
    ((listp y)
     (cond 
       ((member (car y) x) (stable-union ??? (???))) 
       (t (stable-union (append x (??? (???))) (cdr y)))))
    ((not (member y x)) (append x (list y)))
    (t x)))

(编辑在倒数第二行纠正简单的错位,感谢Will Ness发现它)

答案 1 :(得分:1)

你的私奔都混乱了:

(defun stable-union (x y)
  (cond
   ((null x) y)
   ((null y) x)  )     END OF COND form - has no effect

  (do ((i y (cdr i))
      ^^
       (lst3 x (append lst3 
                       (cond
                        ((listp i) 
                         (  (null (member (car i) lst3)) 
                         ^^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ called as a function
                             (cons (car i) nil)           with two arguments
                             nil  ) )
                                 ^^ 
                        (t                         NEXT 3 forms have no effect
                           (null (member i lst3))
                           (cons i nil)
                           nil           )))) )
                                             ^^
        ((null (cdr i)) lst3)))

这是您可能想要的代码,更正了括号,并在需要时添加了一些if

(defun stable-union (x y)
  (cond
   ((null x) y)
   ((null y) x)
   (t
    (do ((i y (cdr i))
         (lst3 x (append lst3 
                   (cond
                     ((listp i) 
                       (if (null (member (car i) lst3))
                           (cons (car i) nil)
                           nil))
                     (t 
                       (if (null (member i lst3))
                           (cons i nil)
                           nil))))))
        ((null (cdr i)) lst3)))))

此代码仍存在问题。您的do逻辑错误,如果它只包含一个元素,它会跳过y中的第一个元素。无论是否需要,您都会一直致电append。请注意,调用(append lst3 nil)会在lst3中复制顶级缺点单元格,完全是多余的。

你所拥有的这么长的陈述通常放在do正文中,而不是放在do的局部变量的更新表格内。


但您可以在适当的情况下使用更专业的do形式。在这里使用dolist是很自然的。在"wvxvw"'s lead on using hash-tables for membership testing之后,我们写道:

(defun stable-union (a b &aux (z (list nil)))
  (let ((h (make-hash-table))
        (p z))
    (dolist (i a)
      (unless (gethash i h)
        (setf (cdr p) (list i) p (cdr p))
        (setf (gethash i h) t)))
    (dolist (i b (cdr z))
      (unless (gethash i h)
        (setf (cdr p) (list i) p (cdr p))
        (setf (gethash i h) t)))))

使用我称之为“head-sentinel”的技术(z变量预先初始化为单个列表)允许自上而下的列表构建的代码的大大简化,代价是分配一个额外的cons单元格。

答案 2 :(得分:0)

(remove-duplicates (append '(a b c) '(e c d)) :from-end t)

答案 3 :(得分:0)

因为你从do开始,并且因为递归解决方案会更糟,所以你可以做到这一点:

(defun union-stable (list-a list-b)
  (do ((i list-b (cdr i))
       filtered back-ref)
      ((null i) (append list-a back-ref))
    (unless (member (car i) list-a)
      (if back-ref
          (setf (cdr filtered) (list (car i))
                filtered (cdr filtered))
          (setf back-ref (list (car i))
                filtered back-ref)))))

这仍然是二次时间,并且行为是这样的:如果第一个列表有重复项,或者第二个列表有重复项(不在第一个列表中),它们将保留。我不确定将此函数称为“联合”是多么公平,但如果它们在尝试统一它们之前有重复,则必须定义如何处理列表。

如果您对结果感兴趣,而不仅仅是锻炼,那么这就是您可能做过的事情。请注意,即使元素在输入列表中重复,它也将确保元素是唯一的。

(defun union-stable-hash (list-a list-b)
  (loop for c = (car (if list-a list-a list-b))
     with back-ref
     with hash = (make-hash-table)
     for key = (gethash c hash)
     with result
     do (unless key
          (if back-ref
              (setf (cdr result) (list c)
                    result (cdr result))
              (when (or list-a list-b)
                (setf back-ref (list c)
                      result back-ref)))
          (setf (gethash c hash) t))
     do (if list-a (setf list-a (cdr list-a))
            (setf list-b (cdr list-b)))
     do (unless (or list-a list-b)
          (return back-ref))))