Ответ 1
Как насчет:
(let [[n m] (split-with (partial not= :b) [:a :b :c :b :d])] (concat n (rest m)))
который разбивает список на: b, а затем удаляет: b и объединяет два списка.
У меня есть список, который может содержать элементы, которые будут сравниваться как равные. Мне нужен подобный список, но с удалением одного элемента. Поэтому из (: a: b: c: b: d) Я хотел бы иметь возможность "удалить" только один: b, чтобы получить (: a: c: b: d).
Контекст - это рука в карточной игре, в которой играют две колоды стандартных карт, поэтому могут быть дубликаты карт, но все равно воспроизводятся по одному.
У меня есть рабочий код, см. ниже. Есть ли более идиоматические способы сделать это в Clojure?
(defn remove-one [c left right]
(if (= right ())
left
(if (= c (first right))
(concat (reverse left) (rest right))
(remove-one c (cons (first right) left) (rest right)))))
(defn remove-card [c cards]
(remove-one c () cards))
Вот ответы Scala, которые я получил некоторое время назад: Что такое идиоматический способ Scala to "remove" один элемент из неизменяемого списка?
Как насчет:
(let [[n m] (split-with (partial not= :b) [:a :b :c :b :d])] (concat n (rest m)))
который разбивает список на: b, а затем удаляет: b и объединяет два списка.
Я обычно решаю эти проблемы с помощью функции более высокого порядка, например split-with
, но кто-то уже это сделал. Иногда это более читаемо или более эффективно работать на более примитивном уровне, поэтому здесь лучше версия исходного кода цикла, используя ленивые последовательности и обобщенные, чтобы использовать предикат для удаления вместо ограничения на проверки равенства:
(defn remove-once [pred coll]
((fn inner [coll]
(lazy-seq
(when-let [[x & xs] (seq coll)]
(if (pred x)
xs
(cons x (inner xs))))))
coll))
user> (remove-once #{:b} [:a :b :c :b :d])
(:a :c :b :d)
Удивительно, что нет API высокого уровня, чтобы сделать что-то вроде этого. Вот еще одна версия, похожая на @amalloy и @James, которая использует recur, чтобы не переполнять стек.
(defn remove-once [x c]
(letfn [(rmv [x c1 c2 b]
(if-let [[v & c] (seq c1)]
(if (and (= x v) b)
(recur x c c2 false)
(recur x c (cons v c2) b))
c2))]
(lazy-seq (reverse (rmv x c '() true)))))
(remove-once :b [:a :b :c :b :d])
;; (:a :c :b :d)