如何从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:我添加了删除的定义。我无法避免使用它而不让函数变得糟糕,所以这是下一个最好的选择。