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

如何从Scheme中的列表中删除不重复的元素?,scheme,Scheme,给出一份清单 (define ll '(a a a b c c c d e e e e)) 我想删除所有非重复元素,只保留重复元素的一个副本,即删除后,结果将是 (a c e) 我的算法是: 遍历列表,比较当前元素和下一个元素 如果它们相等,则cons当前元素和下一个递归调用的列表。比如说, (a a a b c) 从左向右移动,遇到a和a (cons a (remove-nondup (cddr lst))) 否则,跳过当前和下一个元素 (remove-nondup (cddr

给出一份清单

(define ll '(a a a b c c c d e e e e))
我想删除所有非重复元素,只保留重复元素的一个副本,即删除后,结果将是

(a c e)
我的算法是:

  • 遍历列表,比较当前元素和下一个元素

    • 如果它们相等,则
      cons
      当前元素和下一个递归调用的列表。比如说,

      (a a a b c)
      
      从左向右移动,遇到
      a
      a

      (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)))

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


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


以下是一种仅使用标准库函数和尾部调用的方法,尽管它执行线性搜索以查看是否已在结果中看到或放入某个项:

(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)
循环是一种“命名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
执行的额外约束是一个问题,您可以执行非尾部调用或将项目粘贴在列表的末尾,但这很难有效地执行(但使用非标准库宏很容易)。

这里有一种只使用标准库函数和尾部调用的方法,尽管它执行线性搜索以查看是否已在结果中看到或放置项目:

(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)
循环是一种“命名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
执行的额外约束是一个问题,您可以执行非尾部调用或将项目粘贴在列表的末尾,但这很难有效执行(但使用非标准库宏很容易)。

您能给出一个没有预定义库的解决方案吗?在尝试使用库函数之前,我试图先熟悉Scheme。谢谢。@Chan:我添加了
删除
的定义。我无法避免在不使用函数的情况下使用它,所以这是下一个最好的选择。你能给出一个没有预定义库的解决方案吗?在尝试使用库函数之前,我试图先熟悉Scheme。谢谢。@Chan:我添加了
删除
的定义。我无法避免使用它而不让函数变得糟糕,所以这是下一个最好的选择。