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