I am coding with Racket for educational reasons.
I was given a task in which I should create a function that, without filter, would receive a list as an input and return another list only with the even numbers of the first list.
I came up with this recursive definition of an iterative process:
(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))
It works fine. However, I get the result in a reverse order, e.g.:
(add-even '(1 2 3 4 5 6 7))
>> '(6 4 2)
What should I do to have the output in the same order of appearance on the input?
I know how to do it with a reverse function. But that's not a very efficient way..
Of course you can do it without the iter
procedure ...
(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)
But I assume you're using that to keep your add-even
procedure tail-recursive. If that's the case then ...
Your accu
can be a procedure (instead of a list) which fills in a "hole" in your cons
chain. Instead of returning accu
at the end of the computation, you fill in the last value, which in this case is empty
and initialize with identity
instead.
I used bolding to show the parts of your code I changed
(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)
So now you get tail-recursion and you build the list in forward order. I encourage you to step thru the evaluation of this to see how it works. This is continuation passing style.
And perhaps the procedure would be better if you renamed the vars a little bit
(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)
And it cleans up even a little more if you used a 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)
... And it cleans up even more if we used compose
and 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)
In Racket, built-in for/list
with #:when
clause can also be used to create a short function:
(define (onlyeven lst)
(for/list ((i lst) #:when (even? i))
i))
(onlyeven '(1 2 3 4 5 6 7))
; => '(2 4 6)