Clojure Параметры с дополнительными флагами

Какой лучший способ реализовать ключевые слова в качестве необязательных флагов для функции? Я хочу выполнять вызовы функций, например:

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

Используя defn, я могу определить такую ​​функцию, как:

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

Но flags становится списком. Я могу написать свою собственную функцию для поиска в списке, но такая функция не включена в основную библиотеку, поэтому я предполагаю, что она не идиоматична.

Что я сейчас использую:

(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))

Ответы

Ответ 1

Списки (а также векторы и карты) не являются хорошим выбором структуры данных для поиска на основе значений (будет линейное время), поэтому clojure.core не имеет таких функций.

Наборы обеспечивают быстрый поиск по значениям через "contains?", поэтому как насчет

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

Если не будет более одного флага, вы можете использовать деструктурирование следующим образом:

(defn foo [value & [flag]] …)

Ответ 2

clojure.contrib.def включает defnk -macro, что упрощает определение функций с помощью аргументов ключевого слова.

Ответ 3

Вы можете использовать привязку хэш-карты для destructuring дополнительных параметров, таких как:

(defn myfunction 
  [value & {:keys [go-there do-this do-that times] :or {times 1}}]
  {:pre [(integer? times) (< 0 times)]}
  (println "Saw a" value)
  (when go-there
    (dotimes [n times]
      (when do-this (println "Did THIS with" value))
      (when do-that (println "Did THAT with" value)))))

Вышеуказанную функцию можно назвать следующим образом:

(myfunction "foo" :go-there true :do-this true :do-that false :times 5)

Обратите внимание, что вы можете определить значения по умолчанию для ключей с предложением :or {times 1}. Следующий вызов функции будет выполняться только один раз из-за этого значения по умолчанию:

(myfunction "foo" :go-there true :do-this true :do-that false)

Кроме того, Clojure выражения предпосылки позволяют удобно тестировать параметры, что также относится к значениям деструктурированных ключей, поскольку это может можно увидеть в выражении {:pre [...]} сразу после привязки параметров. Следующий вызов не будет выполнен из-за проверки предварительного условия:

(myfunction "foo" :go-there true :do-this true :do-that false :times -1)

Ответ 4

это, строго говоря, не самый эффективный способ записи, но ясно, что

(defn myfunction [value & flags] 
  (cond (contains? (set flags) :a) 1
        (contains? (set flags) :b) 2)

может быть более эффективным фактор (установить флаги) вверх.