删除Scheme中嵌套列表中的重复项

时间:2014-06-16 15:29:07

标签: scheme

我正在尝试在Scheme中编写一个函数make-set,它接受​​一个列表,可能包含列表,并删除所有重复项。例如,(make-set '(1 (2 3) (2 3)))应返回(1 (2 3)),而(make-set '(1 (2 2) 3))应返回(1 (2) 3)。到目前为止,这是我的代码:

(define make-set
  (lambda (lst)
    (cond ((empty? lst) lst)
          ((not (list? lst)) (list lst))
          ((member? (car lst) (cdr lst))
           (cons (car lst)
                   (make-set (delete (car lst) (cdr lst)))))
          ((list? (car lst))
           (cons (make-set (car lst))
                   (make-set (cdr lst))))
          ((equal? (set-len lst) 1)
           lst)
          (else (cons (car lst) (make-set (cdr lst)))))))

适用于我提到过的案例。但是,如果我例如写(make-set '((1 1) (1 1 1)))我得到答案((1) (1)),那么它会创建新的重复项。不知怎的,我想说,如果还有重复,它应该再次运行该功能,但我不知道如何。

(我在make-set中使用的其他函数是member?,它检查一个元素(可以是一个列表)是否在另一个列表中,delete删除所有出现的列表中的元素(可以是列表)和返回列表长度的set-len。)

2 个答案:

答案 0 :(得分:0)

您还没有真正指定在列表的不同级别应该发生什么,例如,如果您从(2 (2 3))开始,但对于您指定的情况,以下情况有效。我们的想法是,您要在每个级别展平元素,然后然后删除重复元素。尽管如此,调用这个make-set有点用词不当,因为它并不是一个你试图回归的集合。否则,((1 1) (1 1 1))可能会返回((1 1) (1 1 1)),因为(1 1)(1 1 1)不同。也就是说,此代码适用于您展示的示例。

(define (remove-duplicates list)
  (if (null? list)
      list
      (let ((tail (remove-duplicates (cdr list))))
        (if (member (car list) tail)
            tail
            (cons (car list) tail)))))

(define (make-set tree)
  (if (not (pair? tree))
      tree
      (remove-duplicates (map make-set tree))))
(make-set '((1 1) (1 1 1)))
;=> ((1))

(make-set '(1 (2 3) (2 3)))
;=> (1 (2 3))

答案 1 :(得分:0)

我同意约书亚的意见,我应该修改算法以避免陷入这种情况。但我会首先回答你提出的问题:不知怎的,我想说如果还有重复,它应该再次运行该功能,但我不知道如何。

实际上这很简单:只需循环直到结果与您传递的列表相同:

(define make-full-set
  (lambda (lst)
    (let ((r (make-set lst)))
      (if (equal? r lst)
          r
          (make-full-set r)))))

测试:

> (make-full-set '((1 1) (1 1 1)))
'((1))

当然这是笨拙和低效的(需要一个额外的通行证和昂贵的列表比较)所以我们真的需要一个更好的算法。根据我的解释,这是我的看法,即所有 atoms 应该是唯一的:

(define (make-set lst)

  (define (sub lst items res)
    (if (null? lst)
        (values items (reverse res))
        (let ((c (car lst)))
          (cond
            ((list? c) (let-values (((items2 res2) (sub c items '())))
                         (sub (cdr lst) items2 (if (null? res2) res (cons res2 res)))))
            ((member c items) (sub (cdr lst) items res))
            (else             (sub (cdr lst) (cons c items) (cons c res)))))))

  (let-values (((_ res) (sub lst '() '()))) res))

测试:

> (make-set '((1 1) (1 1 1)))
'((1))
> (make-set '(2 (2 3)))
'(2 (3))
> (make-set '(2 (2 3)))
'(2 (3))