使用尾递归的过滤函数

时间:2016-02-23 05:29:39

标签: filter scheme racket

目前我有

  (define filter
    (λ (f xs)
      (letrec [(filter-tail
                (λ (f xs x)
                  (if (empty? xs)
                      x
                      (filter-tail f (rest xs)
                                        (if (f (first xs))
                                            (cons (first xs) x)
                                            '()
                                            )))))]
        (filter-tail f xs '() ))))

它应该具有过滤功能

但输出为

(filter positive? '(-1 2 3))
>> (3 2)

但正确的回报应为(2 3)

我想知道代码是否使用尾递归正确完成,如果是,那么我应该使用反向来改变答案?

2 个答案:

答案 0 :(得分:2)

  

我想知道代码是否使用尾递归正确完成。

是的,它正在使用正确的尾调用。

(define (filter-tail f xs x) ...)

其中,内部以递归方式应用于

(filter-tail f
             (some-change-to xs)
             (some-other-change-to x))

并且,从外部它应用于

(filter-tail f xs '())

这两个应用程序都处于尾部位置

  

我应该用反向来改变答案吗?

是的,除非你在构建它时改变列表的尾部(而不是预先设置一个头),否则它无法绕过它。您收到的其中一条评论是使用set-cdr!提到的(另请参阅: Getting rid of set-car! and set-cdr! )。可能还有其他技术,但我不知道它们。我很乐意听到他们的声音。

这是尾递归,需要反转输出。这个使用名为let。

(define (filter f xs)
  (let loop ([ys '()]
             [xs xs])
    (cond [(empty? xs) (reverse ys)]
          [(f (car xs)) (loop (cons (car xs) ys) (cdr xs))]
          [else (loop ys (cdr xs))])))

(filter positive? '(-1 2 3)) ;=> '(2 3)

这是另一个使用左折叠的人。输出仍然需要反转。

(define (filter f xs)
  (reverse (foldl (λ (x ys) (if (f x) (cons x ys) ys))
                  '()
                  xs)))

(filter positive? '(-1 2 3)) ;=> '(2 3)

答案 1 :(得分:0)

使用"difference-lists"技术和curried functions,我们can have

(define (fold c z xs)
   (cond ((null? xs) z)
         (else (fold c (c (car xs) z) (cdr xs)))))

(define (comp f g) (lambda (x)     ; ((comp f g) x)
  (f (g x))))

(define (cons1 x) (lambda (y)      ; ((cons1 x) y)
  (cons x y)))

(define (filter p xs)
  ((fold (lambda (x k)
           (if (p x) 
               (comp k (cons1 x))  ; nesting's on the left
               k))
         (lambda (x) x)            ; the initial continuation, IC
         xs)
    '()))

(display (filter (lambda (x) (not (zero? (remainder x 2)))) (list 1 2 3 4 5)))

这构建

                   comp
              /            \
           comp           cons1 5
        /        \
     comp       cons1 3
    /     \
  IC     cons1 1

并对其应用'(),以高效的从右向左顺序构建结果列表,因此无需撤消它。

首先,fold通过逐个组合consing函数以尾递归方式构建结果列表的差异列表表示;然后,结果函数应用于'()并再次以尾递归的方式通过comp函数组合定义的优点减少,因为组合函数在左侧嵌套 ,因为fold折叠,处理列表从左到右

( (((IC+k1)+k3)+k5) '() )        ; writing `+` for `comp`
  => ( ((IC+k1)+k3) (k5 '()) )   ; and `kI` for the result of `(cons1 I)`
  <= ( ((IC+k1)+k3) l5 )         ; l5 = (list 5)
  => ( (IC+k1) (k3 l5) )
  <= ( (IC+k1) l3 )              ; l3 = (cons 3 l5)
  => ( IC (k1 l3) )
  <= ( IC l1 )                   ; l1 = (cons 1 l3)
  <= l1

fold构建的函数的大小为 O(n),就像临时列表所具有的那样,具有反转。