views:

895

answers:

3

What's the best way to implement keywords as optional flags to a function? I want to make function calls such as:

(myfunction 5)
(myfunction 6 :do-this)
(myfunction 3 :go-here)
(myfunction 2 :do-this :do-that)

Using defn, I can define a function such as:

(defn myfunction [value & flags] ... )

But the flags becomes a list. I can write my own function to search the list, but such a function isn't included in the core library, so I assume it's not idiomatic.

What I'm using now:

(defn flag-set? [list flag] (not (empty? (filter #(= flag %) list))))
(defn flag-add [list flag] (cons flag list))
(defn flag-remove [list flag] (filter #(not= flag %) list))
A: 
Arthur Ulfeldt
+1  A: 

Lists (as well as vectors and maps) are not a good choice of data structure for value-based lookups (will be linear time), that's why clojure.core doesn't have such functions.

Sets do provide fast value-based lookups via "contains?", so how about

(defn foo [value & flags]
  (let [flags (set flags)]
    (if (contains? flags :add-one)
      (inc value)
      value)))

If there won't be more than one flag, you can use destructuring like this:

(defn foo [value & [flag]] …)
achim
Ah, Sets provide contains?. In that case I'll probably just use what I have but change the code for flag-set? to use (contains? (set flags) flag).
Kai
You can abbreviate (contains? flags :add-one) as (flags :add-one) or (:add-one flags).
Jouni K. Seppänen
If you want linear search for lists and vectors, it's in clojure.contrib.seq-utils/find-first. If your list is only a few items long it may be faster to do a linear search than to cast it into a set. Most of the time you're probably going to be passing only a small handful of parameters to your functions.
Brian Carper
+3  A: 

clojure.contrib.def includes the defnk-macro, which makes defining functions with keyword-arguments easier.

pmf