0

I am trying to build filter (a built-in function) using Racket just as a matter of practice.

I created the following code:

(define (filter lista-1 check-function)
 (define (fil-iter lista-1 check-function lista-2)
  (cond ((null? lista-1) lista-2)
        ((check-function (car lista-1)) (fil-iter (cdr lista-1) check-function (append lista-2 (list (car lista-1)))))
        (else (fil-iter (cdr lista-1) check-function lista-2))))
   (trace fil-iter)
   (fil-iter lista-1 check-function '()))

I made a few tests with "odd?", "even?" and "number?" as the "check-function".

All of the outputs were correct. But I might be not seeing something... My intuition says that there is something wrong here.

2 Answers2

0

Your function is correct, only it has a complexity of n² while it could have a complexity of n.

The reason is that you use append instead of cons to build the result, and append has a cost proportional to the length of the list, while cons has a constant cost. So, if you want a function with linear complexity you should write something like:

(define (filter lista-1 check-function)
 (define (fil-iter lista-1 check-function lista-2)
  (cond ((null? lista-1) (reverse lista-2))
        ((check-function (car lista-1))
         (fil-iter (cdr lista-1) check-function (cons (car lista-1) lista-2)))
        (else (fil-iter (cdr lista-1) check-function lista-2))))
   (fil-iter lista-1 check-function '()))

Note that at the end the result must be reversed, but this does not change the complexity of the function since reverse has a linear complexity with the size of the list and is executed only once, at the end of the function.

You can also simplify the function noting that the parameter check-function is not modified at each invocation of the helper fil-iter, so it could be omitted inside it:

(define (filter lista-1 check-function)
 (define (fil-iter lista-1 lista-2)
  (cond ((null? lista-1) (reverse lista-2))
        ((check-function (car lista-1))
         (fil-iter (cdr lista-1) (cons (car lista-1) lista-2)))
        (else (fil-iter (cdr lista-1) lista-2))))
   (fil-iter lista-1 '()))
Renzo
  • 26,848
  • 5
  • 49
  • 61
  • you are my professor! Thanks, another great explanation! –  Oct 18 '16 at 21:53
  • I was avoiding the reverse... Is there another way to use cons instead of append and not using reverse? –  Oct 18 '16 at 22:13
  • Then you can use the techniques shown in [this answer](http://stackoverflow.com/a/40096809/2382734) to one of your question. Another way is by using side-effects, with mutable lists, but this is not typical in functional programming. I really do not understand way you don't want to use `reverse`: when using an accumulator in a tail-recursive function the use of `reverse` is really idiomatic in Scheme and other lisp languages. – Renzo Oct 18 '16 at 23:11
  • Thanks! I am just a beginner. I did not know this was an idiomatic behavior. –  Oct 18 '16 at 23:40
0

One can use for/list with #:when clause to create a filter function:

(define (myfilter proc lst)
  (for/list ((item lst)
             #:when (proc item))
    item))

(myfilter even? '(1 2 3 4 5 6))
; => '(2 4 6)
rnso
  • 23,686
  • 25
  • 112
  • 234