So I’m teaching myself functional programming using Racket Scheme, and I love it so far. As an exercise for myself, I’ve been trying to implement a few simple tasks in a purely functional way. I know immutability is an important part of functional style, but I was wondering if there are any times when it’s okay.
I thought of a fun way for a function to strip non-unique strings from a list of strings when used with filter, shown here:
(define (make-uniquer)
(let ([uniques '()])
(lambda (x)
(if (not (member x uniques))
(set! uniques (cons x uniques))
#f))))
(define (uniquify x)
(let ([uniquer (make-uniquer)])
(filter uniquer x)))
As you can see, make-uniquer returns a closure over a list of strings to compare against for uniqueness, this way it can act as a simple predicate for filter. But I’m destructively updating the closed-over list. Is this bad form, or is it okay to change local closed-over variables in such ways?
In this case, I’d avoid a mutable implementation because a functional implementation can compete pretty well in terms of performance. Here are three versions (including the built-in
remove-duplicates) of the function:The times I get for this are
Not scientific numbers, so take this with a grain of salt. To be fair, I did tune
uniquify-2a bit since my first version was slower. I also improved the mutable version with a hash table, but maybe there are other optimizations that could be applied. Also, the built-inremove-duplicatesis tuned for performance and does use a mutable data structure (though it avoidsset!).You may also be interested in the Guide entry on performance. It points out that use of
set!can harm performance, so use it with care.