Reputation: 820
(filter procedure list)
appliesprocedure
to each element oflist
and returns a new list containing only the elements for whichprocedure
returns true.
(R. Kent Dybvig The Scheme Programming Language) (online)
What may not be apparent from this description is that, while the elements in the returned
list occur in the same order as in list
, the order of calls of procedure
is not
specified in R6RS. (Racket, however, applies the procedure "to each element from first to last")
A recently active answer
mentions that it requires a filterfunc
which works over its argument list
in order. How should one write this function?
An answer with my explanation of the issue is supplied.
Upvotes: 1
Views: 104
Reputation: 15783
It involves you should not use mutation of some external variable inside procedure and it supposes the implementation may apply parallelism, for example map-reduce.
Upvotes: 1
Reputation: 820
What order might a Scheme implementation use, and why? Let's try it (all code run in Chez Scheme REPL):
> (filter (lambda (x) (display x) (even? x))
'(0 1 2 3 4 5)))
452301(0 2 4)
>
list
is a proper list> (filter (lambda (x) (display x) (even? x))
'(0 1 2 . 3)))
Exception in filter: (0 1 2 . 3) is not a proper list
>
> (define xs '(0 1 2 3))
> (set-cdr! (cdddr xs) (cdr xs))
> (filter (lambda (x) (display x) (even? x)) xs)
Exception in filter: (0 1 2 3 1 2 ...) is not a proper list
>
filter
in Chez Scheme which checks these requirements
while filtering, resulting in the "452301" order of predicate applications,
can be seen here> (let ([filter
(lambda (pred? ls)
(let f ([fast ls])
(if (pair? fast)
(let ([rest (f (cdr fast))])
(if (pred? (car fast))
(cons (car fast) rest)
rest))
'())))])
(filter (lambda (x) (display x) (even? x))
'(0 1 2 3 4 5)))
543210(0 2 4)
>
(the identifier fast
is used to match the Chez Scheme code: could be ls
otherwise)
pred?
"from first to last"?rest
variable with an accumulator (compare the following with 3 above;
the changes are small, but filtered elements are cons
ed to acc,
so it has to be reversed to give the result):> (let ([filter
(lambda (pred? ls)
(let f ([fast ls] [acc '()])
(if (pair? fast)
(f (cdr fast)
(if (pred? (car fast))
(cons (car fast) acc)
acc))
(reverse acc))))])
(filter (lambda (x) (display x) (even? x))
'(0 1 2 3 4 5)))
012345(0 2 4)
>
So 4 could be used as the required filterfunc
. It would be an interesting exercise
to add error checks like the Chez Scheme implementation, which is effectively:
(define (filter pred? ls)
(unless (procedure? pred?)
(error #f "not a procedure" pred?))
(or (let f ((pred? pred?) (fast ls) (slow ls))
(if (pair? fast)
(let ((fast1 (cdr fast)))
(if (pair? fast1)
(and (not (eq? fast1 slow))
(let ((fast2 (cdr fast1)))
(let ((rest (f pred? fast2 (cdr slow))))
(and rest
(if (pred? (car fast))
(if (pred? (car fast1))
(if (eq? rest fast2)
fast
(list* (car fast)
(car fast1) rest))
(cons (car fast) rest))
(if (pred? (car fast1))
(if (eq? rest fast2)
fast1
(cons (car fast1) rest))
rest))))))
(and (null? fast1)
(if (pred? (car fast))
fast
'()))))
(and (null? fast) '())))
(error #f "circular/improper list" ls)))
Upvotes: 2