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 clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to