在这种情况下如何在球拍上订购我的累积变量?

How to order my accumulate variable in this case on Racket?

出于教育原因,我正在使用 Racket 进行编码。

我得到了一个任务,我应该创建一个函数,在没有过滤器的情况下,它将接收一个列表作为输入,return另一个列表只包含第一个列表的偶数。

我想到了迭代过程的递归定义:

(define (add-even lista)
  (define (iter lista accu)
    (cond ((null? lista) accu)
          ((even? (car lista)) (iter (cdr lista)
                                     (cons (car lista) accu)))
          (else (iter (cdr lista) accu))))
  (iter lista empty))

它工作正常。但是,我以相反的顺序得到结果,例如:

(add-even '(1 2 3 4 5 6 7))
>> '(6 4 2)

我应该怎么做才能使输出与输入的出现顺序相同?

我知道如何用反向函数来做。但这不是一个非常有效的方法..

当然你可以不用iter程序...

(define (add-even lista)
  (cond ((null? lista) empty)
        ((even? (car lista)) (cons (car lista) (add-even (cdr lista))))
        (else (add-even (cdr lista)))))

(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)

但我假设您正在使用它来保持您的 add-even 过程尾递归。如果是这样的话......


您的 accu 可以是 程序 (而不是列表),它在您的 cons 链中填充 "hole"。您不必在计算结束时返回 accu,而是填写最后一个值,在本例中为 empty 并使用 identity 进行初始化。

我使用 粗体 来显示我更改的代码部分

(define (add-even lista)
  (define (iter lista accu)
    (cond ((null? lista) <b>(accu empty)</b>)
          ((even? (car lista)) (iter (cdr lista)
                                     <b>(λ (rest) (accu </b>(cons (car lista) <b>rest))</b>)))
          (else (iter (cdr lista) accu))))
  (iter lista <b>identity</b>))

(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)

所以现在你得到了尾递归并且你以正向顺序构建列表。我鼓励您逐步完成对此的评估,看看它是如何工作的。这是continuation passing style.


如果您稍微重命名变量,也许程序会更好

(define (add-even lista)
  (define (iter <b>l</b> <b>k</b>)
    (cond ((null? <b>l</b>) (<b>k</b> empty))
          ((even? (car <b>l</b>)) (iter (cdr <b>l</b>)
                                 (λ (rest) (<b>k</b> (cons (car <b>l</b>) rest)))))
          (else (iter (cdr <b>l</b>) <b>k</b>))))
  (iter lista identity))

(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)

如果你使用 named-let

,它会更干净一点
(define (add-even lista)
  (<b>let</b> iter [<b>(l lista)</b> <b>(k identity)</b>]
    (cond ((null? l) (k empty))
          ((even? (car l)) (iter (cdr l)
                                 (λ (rest) (k (cons (car l) rest)))))
          (else (iter (cdr l) k)))))

(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)

... 如果我们使用 composecurry

,它甚至可以清理 更多
(define (add-even lista)
  (let iter [(l lista) (k identity)]
    (cond ((null? l) (k empty))
          ((even? (car l)) (iter (cdr l) (<b>compose</b> k (<b>curry</b> cons (car l)))))
          (else (iter (cdr l) k)))))

(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)

在Racket中,内置的for/list with #:when子句也可以用来创建一个短函数:

(define (onlyeven lst)
  (for/list ((i lst) #:when (even? i))
    i))

(onlyeven '(1 2 3 4 5 6 7))
; => '(2 4 6)