如何从Scheme中的列表中删除非重复元素?

时间:2011-04-22 01:50:46

标签: scheme

给出一个清单,

(define ll '(a a a b c c c d e e e e))

我想删除所有非重复元素,只留下副本的一个副本,即删除后,结果将是

(a c e)

我的算法是:

  • 遍历列表,将当前元素与下一个元素进行比较。

    • 如果它们相等,那么cons当前元素与下一个递归调用的列表。例如,

      (a a a b c)
      

      从左向右移动,遇到aa

      (cons a (remove-nondup (cddr lst)))
      
    • 否则,跳过当前和下一个元素。

      (remove-nondup (cddr lst))
      

我遇到的问题是

(define (remove-nondup lst)
  (if (>= (length lst) 2)
      (if (eq? (car lst) (cadr lst))
          (cons (car lst) (remove-nondup (cdr lst)))
          (remove-nondup (cddr lst)))
      lst))

我遇到的问题是如果有超过3个连续元素,我无法跟踪前一个元素。所以我想我应该使用另一个程序来删除所有重复项?或者我可以把它们放到一个程序中?

所以我当前的替代解决方案是,

(define (remove-dup lst)
  (if (>= (length lst) 2)
      (if (eq? (car lst) (cadr lst))
          (cons (car lst) (remove-dup (cddr lst)))
          (cons (car lst) (remove-dup (cdr lst))))
      lst))

(define (remove-nondup-helper lst)
  (if (>= (length lst) 2)
      (if (eq? (car lst) (cadr lst))
          (cons (car lst) (remove-nondup-helper (cdr lst)))
          (remove-nondup (cddr lst)))
      lst))

; call the helper function and remove-dup
(define (remove-nondup lst)
  (remove-dup (remove-nondup-helper lst)))

2 个答案:

答案 0 :(得分:1)

这是我的解决方案:首先,抓住bagify(任何版本都可以)。然后:

(define (remove-singletons lst)
  (define (singleton? ass)
    (< (cdr ass) 2))
  (map car (remove singleton? (bagify lst))))

remove来自SRFI 1.如果您使用的是Racket,请先运行(require srfi/1)。或者,使用这个简单的定义:

(define remove #f)   ; Only needed in Racket's REPL
(define (remove pred lst)
  (cond ((null? lst) lst)
        ((pred (car lst)) (remove pred (cdr lst)))
        (else (cons (car lst) (remove pred (cdr lst))))))

答案 1 :(得分:0)

这是一种仅使用标准库函数和尾部调用的方法,但它执行线性搜索以查看项目是否已被查看或放入结果中:

(define remove-nondup
  (λ (ls)
    (reverse
      (let loop ([ls ls] [found '()] [acc '()])
        (cond
          [(null? ls)
            acc]
          [(memq (car ls) found)
            (loop (cdr ls)
                  found
                  (if (memq (car ls) acc)
                      acc
                      (cons (car ls) acc)))]
          [else
            (loop (cdr ls)
                  (cons (car ls) found)
                  acc)])))))

(remove-nondup '(a a a b c c c d e e e e)) =>
  (a c e)
(remove-nondup '(a a a b c c c d e e e e f a a f)) =>
  (a c e f)

loop是一个“命名为let”:一种在程序中粘贴辅助程序而不会产生大量语法混乱的方便方法。

如果您只想将连续重复项缩小到一个项目,并且仅在连续两次没有出现项目时删除项目,那么这里有一种方法可以在两个单元格之前“记住”该项目搜索它,并仅使用尾调用:

(define remove-nonconsecdup
  (λ (ls)
    (reverse
      (letrec (
          [got1 (λ (ls prev acc)
                  (cond
                    [(null? ls)
                      acc]
                    [(eq? prev (car ls))
                      (got2 (cdr ls) (cons prev acc))]
                    [else
                      (got1 (cdr ls) (car ls) acc)]))]
          [got2 (λ (ls acc)
                  (cond
                    [(null? ls)
                      acc]
                    [(eq? (car acc) (car ls))
                      (got2 (cdr ls) acc)]
                    [else
                      (got1 (cdr ls) (car ls) acc)]))])
        (if (null? ls)
            '()
            (got1 (cdr ls) (car ls) '()))))))

(remove-nonconsecdup '(a a a b c c c d e e e e)) =>
  (a c e)
(remove-nonconsecdup '(a a a b c c c d e e e e f a a f)) =>
  (a c e a)

我不喜欢倒转列表,但调用reverse很容易。如果由reverse完成的额外收费是一个问题,你可以进行非尾调用或将项目粘贴在列表的末尾,但这样做更难有效(但使用非标准库很容易)宏)。