Накопить значения в список - PullRequest
0 голосов
/ 27 февраля 2019

Я хотел бы создать список в зависимости от результатов некоторых функций.В Java (мой опыт) я бы сделал что-то вроде:

List<String> messages = ...

if(condition 1)
   messages.add(message 1);

if(condition 2)
   messages.add(message 2);

...

if(condition N)
   messages.add(message N);

В clojure я думаю, что мне нужно будет создать список, используя let, как показано ниже (просто фиктивный пример):

(let [result
  (vec
    (if (= 1 1) "message1" "message2")
    (if (= 1 0) "message3" "message4"))]
result)

Я также проверил cond, но мне нужно добавить элементы в список с учетом всех проверок (и cond разрывается после выполнения одного условия).

Каким путем я должен следовать, чтобы достичь этого?

Ответы [ 6 ]

0 голосов
/ 01 марта 2019

Типичным случаем является проверка части данных для создания списка ошибок.

Я бы создал таблицу, которая отображает условие на сообщение:

 (def error->message-table
  {condition1 message1
   condition2 message2
   ...})

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

Несмотря на то, что вы реализуете таблицу, все, что нам нужно сделать, это собрать сообщения для применимых предикатов:

(defn messages [stuff]
  (->> error->message-table
       (filter (fn [pair] ((first pair) stuff)))
       (map second)))

Без последовательного примера трудно быть более явным.

Первоклассные функции и упакованные управляющие структуры в filter и map дают нам возможность кратко и ясно выразить алгоритм, изолируя контент в структуру данных.

0 голосов
/ 27 февраля 2019

Я вижу несколько ответов, указывающих на макрос cond->, который, по-видимому, наиболее точно соответствует вашему запросу, поскольку он наиболее близок к стилю, указанному в вашем вопросе.

В зависимости от количества условий, которые у вас есть,Ваш вопрос кажется хорошим кандидатом для простого использования filter.

(def nums (range 10))
(filter #(or (even? %) (= 7 %)) nums)

Если у вас есть набор условий (функций), и «или» их совместное использование будет громоздким, вы можете использовать некоторые-ф .

Числа от 0 до 19, которые либо четны, делятся на 7, больше 17 или точно равны 1. Глупый пример, который я знаю, просто хотел показать простой вариант использования.

(filter (some-fn
         even?
         #(zero? (mod % 7))
         #(> % 17)
         #(= 1 %))
        (range 20))
0 голосов
/ 27 февраля 2019

Не обязательно относится к вашему варианту использования и, конечно, не является основным решением, но время от времени мне нравятся условные выражения cl-format:

(require '[clojure.pprint :refer [cl-format]])
(require '[clojure.data.generators :as g])
(cl-format nil 
           "~:[He~;She~] ~:[did~;did not~] ~:[thought about it~;care~]"
           (g/boolean) (g/boolean) (g/boolean))
0 голосов
/ 27 февраля 2019

Похоже, у всех была одна и та же идея!Я сделал мой с ключевыми словами:

(ns tst.demo.core
  (:use tupelo.core demo.core tupelo.test))

(defn accum
  [conds]
  (cond-> []   ; append to the vector in order 1,2,3
    (contains? conds :cond-1) (conj :msg-1)
    (contains? conds :cond-2) (conj :msg-2)
    (contains? conds :cond-3) (conj :msg-3)))

(dotest
  (is= [:msg-1]                 (accum #{:cond-1}))
  (is= [:msg-1 :msg-3]          (accum #{:cond-1 :cond-3}))
  (is= [:msg-1 :msg-2]          (accum #{:cond-2 :cond-1}))
  (is= [:msg-2 :msg-3]          (accum #{:cond-2 :cond-3}))
  (is= [:msg-1 :msg-2 :msg-3]   (accum #{:cond-3 :cond-2 :cond-1 })) ; note sets are unsorted
)

Если вы хотите больше энергии, вы можете использовать cond-it-> из библиотеки Тупело .Он пропускает целевое значение через и условие и , которое образует действие, и использует специальный символ it, чтобы показать, гдерезьбовое значение должно быть размещено.Этот модифицированный пример показывает 4-е условие, где "msg-3 завидует msg-1" и всегда загружает его из результата:

(ns tst.demo.core
  (:use tupelo.core demo.core tupelo.test))

(defn accum
  [conds]
  (cond-it-> #{}  ; accumulate result in a set
    (contains? conds :cond-1) (conj it :msg-1)
    (contains? conds :cond-2) (conj it :msg-2)
    (contains? conds :cond-3) (conj it :msg-3)
    (contains? it :msg-3)     (disj it :msg-1) ; :msg-3 doesn't like :msg-1
  ))

; remember that sets are unsorted
(dotest
  (is= #{:msg-1}          (accum #{:cond-1}))
  (is= #{:msg-3}          (accum #{:cond-1 :cond-3}))
  (is= #{:msg-1 :msg-2}   (accum #{:cond-2 :cond-1}))
  (is= #{:msg-2 :msg-3}   (accum #{:cond-2 :cond-3}))
  (is= #{:msg-2 :msg-3}   (accum #{:cond-3 :cond-2 :cond-1 })) 
)
0 голосов
/ 27 февраля 2019

Если вы хотите, чтобы они были условно добавлены, как в примере с Java, вы можете использовать cond->, который не замыкает накоротко:

(let [messages []]
  (cond-> messages ; Conditionally thread through conj
    (= 1 1) (conj "Message1")
    (= 0 1) (conj "Message2")
    (= 0 0) (conj "Message3")))

=> ["Message1" "Message3"]

Если вы хотите условно добавить одну или другую, какваш второй пример показывает, что вы можете просто использовать обычный conj с некоторыми if выражениями:

(let [messages []]
  (conj messages
    (if (= 1 1) "Message1" "Message2")
    (if (= 0 1) "Message3" "Message4")))

=> ["Message1" "Message4"]

И я отмечу, что ваша первоначальная попытка почти сработала.Вместо vec вы могли бы использовать vector или просто векторный литерал:

(let [messages [(if (= 1 1) "Message1" "Message2")
                (if (= 1 0) "Message3" "Message4")]]
  messages)

=> ["Message1" "Message4"]

Хотя это было бы полезно только в том случае, если у вас еще не было заполнено messages, чтоВы хотели добавить в.Если бы это было так, вы должны использовать concat или into:

(let [old-messages ["old stuff"]
      messages [(if (= 1 1) "Message1" "Message2")
                (if (= 1 0) "Message3" "Message4")]]
  (into old-messages messages))

=> ["old stuff" "Message1" "Message4"]
0 голосов
/ 27 февраля 2019

Взгляните на cond -> .

Например, ваш пример Java может быть написан так:

(cond-> (some-fn-returning-messages)
  (= 1 1) (conj "message1")
  (= 1 2) (conj "message2")
  ...
  (= 1 n) (conj "messagen"))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...