【问题标题】:Scheme Guile: remove only first occurrence from a list (non destructive)Scheme Guile:仅从列表中删除第一次出现(非破坏性)
【发布时间】:2015-05-30 04:03:35
【问题描述】:

我需要以非破坏性的方式从列表中删除第一次出现的元素。

根据Guile Reference Manual,有一组函数可以破坏性方式(delq1!、delv1!、delete1!)。另一方面,非破坏性版本会删除所有出现的元素。

我知道我可以用几行代码编写一个函数(可能是通过过滤器)来完成它,但我想知道是否存在更好/标准的方法来完成它。

举个例子,给出列表

((1 2) (3 4) (1 2)) 

移除元素时

(1 2) 

我希望结果是

((3 4) (1 2)) 

而原始列表仍然存在

((1 2) (3 4) (1 2)).

提前致谢!

【问题讨论】:

    标签: list scheme guile


    【解决方案1】:

    这里有五个版本的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))))))
    

    第三个包含(append (reverse l) m) 的融合,并在到达列表末尾时删除无用的(reverse a),但它仍会在a 中累积列表的副本(reverse a)

     (define (append-reverse l m) 
       (if (null? l)
           m
           (append-reverse (cdr l) (cons (car l) m))))
    
     (define (delete1c v ls)
       (let loop ((l ls) (a '()))
         (if (null? l) ls
             (if (equal? v (car l)) 
                 (append-reverse a (cdr l))
                 (loop (cdr l) (cons (car l) 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) 替换,然后(append-reverse (take-reverse a b) c):

    (define (append-reverse l m) 
      (if (null? l)
          m
          (append-reverse (cdr l) (cons (car l) m))))
    
    (define (take-reverse l n)
      (let loop ((l l) (n n) (a '()))
        (if (= n 0) a
            (if (null? l) a
                (loop (cdr l) (- n 1) (cons (car l) a))))))
    
    (define (delete1e v ls)
      (let loop ((l ls) (a 0))
        (if (null? l) ls
            (if (equal? v (car l)) 
                (append-reverse (take-reverse ls a) (cdr l))
                (loop (cdr l) (+ a 1))))))
    

    这样做的结果是遍历列表一次,如果没有找到值,则不进行复制,如果找到了值,则仅在元素被遍历和复制两次之前的列表的前缀。一次在take-reverse,然后一次在append-reverse

    如果允许破坏性函数,则可以使用append-reverse! 而不是append-reverse 将列表前缀的复制安全地减少到一次,如(append-reverse (take-reverse ls a) (cdr l))(take-reverse ls a) 的结果是@987654344 线性使用的副本@。有类型系统可以证明这是安全的:

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

    这会遍历列表一次并且不会复制。

    【讨论】:

      【解决方案2】:

      在Scheme 中,标准解决方案是创建一个新列表,但没有元素。在documentation 中,我们看到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))
      

      【讨论】:

      • 您的解决方案在某处缺少cons。无论如何,OP想知道是否有一种内置的方法来实现它,但没有。 :-(
      • 太棒了!有用!非常感谢!我只想指出,为了完成帖子,在示例中它应该是 (delete-1st '(1 2) '((1 2) (3 4) (1 2)))
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-25
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 2012-11-09
      相关资源
      最近更新 更多