On Sep 29, 2010, at 3:55 AM, nickikt wrote:
>
>
> (defun find-all (item sequence &rest keyword-args
> &key (test #'eql) test-not &allow-other-keys)
> "Find all those elements of sequence that match item,
> according to the keywords. Doesn't alter sequence."
> (if test-not
> (apply #'remove item sequence
> :test-not (complement test-not) keyword-args)
> (apply #'remove item sequence
> :test (complement test) keyword-args)))
>
Both Clojure and Common Lisp have a function to remove a value from a sequence.
However, the CL version supports quite a few more options, which accounts for
the complexity of this FIND-ALL function.
The function 'remove' in both languages obviously has to take an item to look
for and a sequence to search. But the CL version also accepts various keyword
arguments that tailor how it behaves. There is a default test which determines
whether the item matches a given element, but another test can be specified by
the :test keyword. This allows for various flavors of equality when matching.
There is also a :test-not keyword which works in the opposite sense. You can
see both of those keywords above. The CL version also accepts other keywords
such as :start, :end, :from-end, :count, and :key.
In the section after the lambda-list keyword &key above you can see that
FIND-ALL also accepts :test and :test-not keywords, with a default :test of the
function EQL. The lambda-list keyword &allow-other-keys is used to show that
FIND-ALL will also accept the other REMOVE keywords and pass them along.
Before the &key section, however, there is the &rest section. What actually
happens is that every argument passed to FIND-ALL, besides the first two
required arguments, is captured as a list in the variable KEYWORD-ARGS. CL also
processes the :test and :test-not keyword arguments separately, but they and
their keywords are also bundled up in KEYWORD-ARGS. This makes it easy to pass
everything along to REMOVE via the APPLY form.
Here is a Clojure implementation that is pretty true to the spirit of the CL
version. All of the optional arguments are captured in the list 'keyword-args',
but we also break out the keywords into a map:
(defn find-all [item coll & keyword-args]
(let [keywords (apply hash-map keyword-args)]
(if (:test-not keywords)
(remove (fn [elt] ((:test-not keywords) item elt)) coll)
(remove (fn [elt] (not ((:test keywords) item elt))) coll))))
Since we want to find things that match our criterion we reverse the sense of
the :test and remove things that fail our criterion. But we have to do the
opposite with the :test-not case here since Clojure's 'remove' doesn't
understand that :test-not is the opposite of :test. Notice also that we can
call 'remove' directly (without 'apply') since we aren't passing in a list of
keywords.
(find-all 1 '(1 2 3 2 1) :test =) => (1 1)
(find-all 1 '(1 2 3 2 1) :test not=) => (2 3 2)
(find-all 1 '(1 2 3 2 1) :test-not =) => (2 3 2)
Have all good days,
David Sletten
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en