Подтвердить что ты не робот

Clojure Ключевое слово и дополнительная аргументация

Я хочу создать функцию, которая принимает требуемый аргумент x, и либо необязательный аргумент opt1 ИЛИ аргумент ключевого слова opt2.

Сейчас у меня

(defn foo x & [opt1 {:keys [opt2]}]
  ...

Но указанная выше подпись позволяет мне передать аргумент ключевого слова opt2, когда присутствуют как x, так и opt1, такие как

(foo 'x 'opt1 {:opt2 'opt2})

не нравится

(foo 'x {:opt2 'opt2})

Пожалуйста, помогите мне создать функцию, которая принимает требуемый аргумент X и opt1 или opt2, где opt2 является аргументом ключевого слова.

Спасибо.

EDIT: Я хочу сделать то же самое и для других макросов. Поэтому мне все еще нужно использовать defmacro.

4b9b3361

Ответ 1

Проблема заключается в двусмысленности. Рассмотрим функцию (fn foo [x y & args]), которая принимает два необязательных аргумента, а затем любое количество аргументов ключевого слова. Если вы затем назовете это как (foo :bar :baz), как ваша программа справится с этим? x = > :bar, y = > :baz? Или x и y не предоставлены, с единственным аргументом ключевого слова :bar = > :baz?

Даже в Common Lisp, который, возможно, обладает большей гибкостью, чем Clojure при анализе параметров функции, смешение необязательных аргументов и ключевых слов не рекомендуется, по крайней мере, одна популярная книга.

Лучше всего изменить все свои аргументы на позиционные аргументы или все ваши параметры на аргументы ключевых слов. Если вы используете аргументы ключевого слова, вы можете использовать деструктурирование хэш-карты для предоставления значений по умолчанию для необязательных параметров ключевого слова.

user> (defn foo [& {:keys [x y bar] 
                    :or {x 1 y 2 bar 3}}] 
        (prn [x y bar]))
#'user/foo
user> (foo)
[1 2 3]
nil
user> (foo :bar :baz)
[1 2 :baz]
nil

Ответ 2

вам нужно проверить, являются ли дополнительные аргументы аргументами ключевого слова или нет (я предполагаю, что ваш или является эксклюзивным или), поэтому вы можете сделать это следующим образом:

(defn foo [& args] 
    (if (= (count args) 1)
        (let [[opt1] args] (println opt1))
        (let [{:keys [opt2]} args] (println opt2))))

проверьте аргументы, если они являются аргументами ключевого слова или нет. Поскольку у вас есть только один необязательный параметр, просто: проверьте, требуется ли только одно из аргументов ключевого слова для двух.