Есть ли резьбовой способ выполнения условия "или" (или любого другого теста) в clojure? - PullRequest
3 голосов
/ 15 мая 2019

Что-то вроде:

(or->
    "foobar"
    (clojure.string/starts-with? "foo")
    (clojure.string/starts-with? "bar"))

=> true

Если нет, то каков идиоматический способ выполнения теста с несколькими ors / ands?

Ответы [ 3 ]

9 голосов
/ 15 мая 2019

Я бы использовал some здесь.По сути, это дружественная последовательность or проверка, которая может помочь сократить дублирование, когда все условия в основном одинаковы:

(some #(clojure.string/starts-with? "foobar" %)
      ["foo" "bar"])

Используется ранний возврат.


and эквивалент some будет every?:

(every? odd? [3 5 9])
=> true
3 голосов
/ 16 мая 2019

Нам нужно вставить первый аргумент во все последующие предложения перед их оценкой. Таким образом, решение должно быть макросом. Если мы готовы обойтись без проверки синтаксиса, мы можем определить ее довольно кратко:

(defmacro or-> [arg & pred-exprs]
  (let [insert-expr (fn [[x & xs]] (list* x arg xs))
        inserted-exprs (map insert-expr pred-exprs)]
    (cons 'or inserted-exprs)))

Работает:

problem=> (or-> "foobar" 
                (clojure.string/starts-with? "foo")
                (clojure.string/starts-with? "bar"))
true

problem=> (or-> "foobar"
                (clojure.string/starts-with? "for")
                (clojure.string/starts-with? "bar"))
false

Но он оценивает форму arg для каждого предпринятого предложения. Чтобы оценить форму arg только один раз, мы вставляем форму let для gensym:

(defmacro or-> [arg & pred-exprs]
  (let [arg-sym (gensym)
        insert-arg (fn [[x & xs]] (list* x arg-sym xs))
        inserted-exprs (map insert-arg pred-exprs)]
    `(let [~arg-sym ~arg] (or ~@inserted-exprs))))

Я бы хотел сделать это с помощью автогензима, но я могу понять, как это сделать. Однако это сделано, это не многопоточность в обычном смысле Clojure, который всегда выполняет все предложения с резьбой.

2 голосов
/ 15 мая 2019

В общем случае вам действительно нужно использовать or и символ:

(defn check [val]
  (or
    (= val "foobar")
    (clojure.string/starts-with? val "foo")
    (clojure.string/starts-with? val "bar")))

(check "foobar") => true
(check "foosball") => true
(check "barbell") => true
(check "egg-foo-yung") => false
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...