List Scheme Guile:仅从列表中删除第一个匹配项(非破坏性)
我需要以非破坏性的方式从列表中删除元素的第一次出现 根据,有一组函数可以以破坏性的方式执行此操作(delq1!、delv1!、delete1!)。另一方面,非破坏性版本将删除元素的所有引用 我知道我可以写一个函数(可能是通过过滤器)在几行内完成,但我想知道是否有更好的/标准的方法来完成 举个例子,给出清单List Scheme Guile:仅从列表中删除第一个匹配项(非破坏性),list,scheme,guile,List,Scheme,Guile,我需要以非破坏性的方式从列表中删除元素的第一次出现 根据,有一组函数可以以破坏性的方式执行此操作(delq1!、delv1!、delete1!)。另一方面,非破坏性版本将删除元素的所有引用 我知道我可以写一个函数(可能是通过过滤器)在几行内完成,但我想知道是否有更好的/标准的方法来完成 举个例子,给出清单 ((1 2) (3 4) (1 2)) 移除元件时 (1 2) 我希望结果是好的 ((3 4) (1 2)) 而原来的名单仍然存在 ((1 2) (3 4) (1 2)). 提前
((1 2) (3 4) (1 2))
移除元件时
(1 2)
我希望结果是好的
((3 4) (1 2))
而原来的名单仍然存在
((1 2) (3 4) (1 2)).
提前谢谢 在Scheme中,标准解决方案是创建一个新列表,但不包含元素。在中,我们看到
delete
过程消除了元素的所有出现-因此我们必须推出自己的解决方案,但很简单:
(define (delete-1st x lst)
(cond ((null? lst) '())
((equal? (car lst) x) (cdr lst))
(else (cons (car lst)
(delete-1st x (cdr lst))))))
例如:
(delete-1st '(1 2) '((1 2) (3 4) (1 2)))
=> '((3 4) (1 2))
这里有五个版本的
delete1
和一个版本的delete1代码>。每个元素从返回的列表中删除第一个与给定元素相等的元素。后者在时间和空间上更为理想。不使用破坏性功能,除非在最后一部分中考虑了其正确使用
第一种是非尾部递归:
(define (delete1a v l)
(let loop ((l l))
(if (null? l) '()
(if (equal? v (car l))
(cdr l)
(cons (car l) (loop (cdr l)))))))
然后,第二个和第三个是尾部递归的,但效率低下,因为它们复制的内容超出了需要。例如,如果未找到该项,则复制整个列表
(define (delete1b v l)
(let loop ((l l) (a '()))
(if (null? l) (reverse a)
(if (equal? v (car l))
(append (reverse a) (cdr l))
(loop (cdr l) (cons (car l) a))))))
第三个包含(追加(反向l)m)
的融合,当到达列表末尾时删除无用的(反向a)
,但它仍然在a
中累积该(反向a)
所需的列表副本:
在第四种版本中,累加器由计数器代替:
(define (delete1d v ls)
(let loop ((l ls) (a 0))
(if (null? l) ls
(if (equal? v (car l))
(append (take ls a) (cdr l))
(loop (cdr l) (+ a 1))))))
当take
以递归方式实现时,它必须反转其结果,但我们已经有了一个append reverse
,那么为什么不使用一个take reverse
以相反的顺序返回其结果呢?因此(append(take a b)c)
被(append(reverse(take reverse a b))c)替换,然后被(附加反向(取反向a b)c)
:
这样做的结果是,列表被遍历一次,如果没有找到值,并且如果在元素被遍历和复制两次之前只找到了列表的前缀,则不进行复制。在中执行一次反向操作,然后在中执行一次追加反向操作
如果允许使用破坏性函数,则可以使用追加反向!
而不是追加反向
将列表前缀的复制安全地减少到一次,如(追加反向(取反向ls a)(cdr l))
中所述(取反向ls a)
的结果是线性使用的副本(追加反向r(cdr l))
。有一些类型的系统可以证明这是安全的:
(define (append-reverse! l m)
(if (null? l)
m
(let ((next (cdr l)))
(begin (set-cdr! l m)
(append-reverse next l)))))
但我们可能会更进一步,定义delete1!
:
(define (delete1! v l)
(let loop ((left '()) (right l))
(if (null? right) l
(if (equal? (car right) v)
(if (null? left) (cdr right)
(begin (set-cdr! left (cdr right))
l))
(loop right (cdr right))))))
这只会遍历列表一次,不会进行复制。您的解决方案在某个地方缺少一个cons
。无论如何,OP想知道是否有一种内置的方法来实现它,但实际上没有。:-(太好了!它很有效!非常感谢!为了完成这篇文章,我只想指出在示例中它应该是(delete-1st'(1-2) '((1 2) (3 4) (1 2)))
(define (append-reverse! l m)
(if (null? l)
m
(let ((next (cdr l)))
(begin (set-cdr! l m)
(append-reverse next l)))))
(define (delete1! v l)
(let loop ((left '()) (right l))
(if (null? right) l
(if (equal? (car right) v)
(if (null? left) (cdr right)
(begin (set-cdr! left (cdr right))
l))
(loop right (cdr right))))))