如何在Racket上订购我的累积变量?

时间:2016-10-17 21:58:04

标签: functional-programming lisp racket sicp

出于教育原因,我正在使用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)

如何在输入上以相同的外观顺序输出输出?

我知道如何使用反向功能。但这不是一种非常有效的方式..

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)

...如果我们使用composecurry

,它会清除甚至更多
(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)