使用模式删除列表中的元素

时间:2014-06-22 19:20:51

标签: list scheme racket

问候所有人。我试图在Racket中编写算法,但我遇到了一个问题:

我正在研究在表面上生成不同类型网格的方法,使用CAD软件作为Racket的后端。基本上我有一个函数可以生成参数曲面的点坐标矩阵(在u和v域中),另一个函数用一定的顺序将这些点与一条线连接起来,以创建网格图案。问题是,为了获得更复杂的网格,我需要能够从该矩阵中删除某些点。

话虽如此,我有一个数据列表(在我的情况下是点),我想根据 true-false-false-true 模式从该列表中删除项目。例如,给定列表'(0 1 2 3 4 5 6 7 8 9 10)算法将保留第一个元素,删除接下来的两个,保留第三个元素然后迭代相同的模式以用于其余的列表,作为最终结果返回列表'(0 3 4 7 8)。

有什么建议吗?谢谢。

4 个答案:

答案 0 :(得分:2)

使用SRFI-1中的列表函数的解决方案:

#!racket
(require srfi/1)
(define (pattern-filter pat lst)
  (fold-right (λ (p e acc) (if p (cons e acc) acc))
              '()
              (apply circular-list pat)
              lst))

(pattern-filter '(#t #f #f #t)  
                '(0 1 2 3 4 5 6 7 8 9 10)) ; ==> '(0 3 4 7 8)

还有其他方法,但不会变得更容易阅读。

答案 1 :(得分:2)

在Racket中,我可能会使用match来表达您描述的特定模式:

#lang racket

(define (f xs)
  (match xs
    [(list* a _ _ d more) (list* a d (f more))]
    [(cons a _)           (list a)]
    [_                    (list)]))

(require rackunit)
;; Your example:
(check-equal? (f '(0 1 2 3 4 5 6 7 8 9 10)) '(0 3 4 7 8))
;; Other tests:
(check-equal? (f '())           '())
(check-equal? (f '(0))          '(0))
(check-equal? (f '(0 1))        '(0))
(check-equal? (f '(0 1 2))      '(0))
(check-equal? (f '(0 1 2 3))    '(0 3))
(check-equal? (f '(0 1 2 3 4))  '(0 3 4))

然而,我也喜欢(和赞成)usepla和Sylwester的答案,因为它们概括了模式。


更新:我的原始示例使用了(list a _ _ d more ...)(list a _ ...)匹配模式。 But that's slow!分别使用(list* a _ _ d more)(cons a _)。这扩展为您使用cond和列表基元手动编写的快速代码。

答案 2 :(得分:2)

使用Racket的for循环:

(define (pattern-filter pat lst)
  (reverse
   (for/fold ((res null)) ((p (in-cycle pat)) (e (in-list lst)))
     (if p (cons e res) res))))

测试

> (pattern-filter '(#t #f #f #t)  '(0 1 2 3 4 5 6 7 8 9 10))
'(0 3 4 7 8)

答案 3 :(得分:1)

这个问题都标有,因此除了适用于Racket的版本之外,让一个可以在Scheme中运行的实现可能并不是一个坏主意。在其他一些答案中。这使用了在其他一些答案中看到的相同类型的方法:创建无限重复的布尔模式并迭代它和输入列表,保持模式为真的元素。

这是一个获取元素列表和#t和#f列表的方法,并返回与模式中#t位于同一位置的元素列表。只要元素或模式没有更多元素,它就会结束。

(define (keep elements pattern)
  ;; Simple implementation, non-tail recursive
  (if (or (null? elements)
          (null? pattern))
      '()
      (let ((tail (keep (cdr elements) (cdr pattern))))
        (if (car pattern)
            (cons (car elements) tail)
            tail))))

(define (keep elements pattern)
  ;; Tail recursive version with accumulator and final reverse
  (let keep ((elements elements)
             (pattern pattern)
             (result '()))
    (if (or (null? elements)
            (null? pattern))
        (reverse result)
        (keep (cdr elements)
              (cdr pattern)
              (if (car pattern)
                  (cons (car elements) result)
                  result)))))

为了获得适当的重复模式,我们可以创建一个表单的循环列表(#t #f #f #t ...)我们可以创建一个列表(#t #f #f #t),然后破坏性地连接它与自己使用nconc。 (我称之为nconc,因为我已经有了一个Common Lisp背景。在Scheme中,它可能更适合称之为追加!)

(define (nconc x y)
  (if (null? x) y
      (let advance ((tail x))
        (cond
         ((null? (cdr tail))
          (set-cdr! tail y)
          x)
         (else 
          (advance (cdr tail)))))))
(let ((a (list 1 2 3)))
  (nconc a a))
;=> #0=(1 2 3 . #0#)

因此,我们有一个解决方案:

(let ((patt (list #t #f #f #t)))
  (keep '(0 1 2 3 4 5 6 7 8 9 0) (nconc patt patt)))
;=> (0 3 4 7 8)