出于教育原因,我正在使用Racket进行编码。
我被赋予了一个任务,我应该在其中创建一个函数,在没有过滤器的情况下,将接收列表作为输入,并仅使用第一个列表的偶数返回另一个列表。
我想出了一个迭代过程的递归定义:
(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)
如何在输入上以相同的外观顺序输出输出?
我知道如何使用反向功能。但这不是一种非常有效的方式..
答案 0 :(得分:3)
当然,您可以在没有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
链中的“漏洞”。您不必在计算结束时返回accu
,而是填写最后一个值,在这种情况下为empty
,并使用identity
进行初始化。
我使用加粗来显示我更改的代码部分
(define (add-even lista)
(define (iter lista accu)
(cond ((null? lista) (accu empty))
((even? (car lista)) (iter (cdr lista)
(λ (rest) (accu (cons (car lista) rest)))))
(else (iter (cdr lista) accu))))
(iter lista identity))
(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)
所以现在你得到了尾递归,你以正向顺序构建列表。我鼓励你通过对此的评估来看看它是如何工作的。这是continuation passing style。
如果你稍微重新命名变量,那么程序可能会更好
(define (add-even lista)
(define (iter l k)
(cond ((null? l) (k empty))
((even? (car l)) (iter (cdr l)
(λ (rest) (k (cons (car l) rest)))))
(else (iter (cdr l) k))))
(iter lista identity))
(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)
如果您使用named-let
(define (add-even lista)
(let iter [(l lista) (k identity)]
(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)
...如果我们使用compose
和curry
(define (add-even lista)
(let iter [(l lista) (k identity)]
(cond ((null? l) (k empty))
((even? (car l)) (iter (cdr l) (compose k (curry cons (car l)))))
(else (iter (cdr l) k)))))
(add-even '(1 2 3 4 5 6 7))
; => '(2 4 6)
答案 1 :(得分:0)
在Racket中,带有for/list
子句的内置#:when
也可用于创建短函数:
(define (onlyeven lst)
(for/list ((i lst) #:when (even? i))
i))
(onlyeven '(1 2 3 4 5 6 7))
; => '(2 4 6)