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

Что такое idiomatic Clojure для "удаления" одного экземпляра из многих в списке?

У меня есть список, который может содержать элементы, которые будут сравниваться как равные. Мне нужен подобный список, но с удалением одного элемента. Поэтому из (: 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" один элемент из неизменяемого списка?

4b9b3361

Ответ 1

Как насчет:

(let [[n m] (split-with (partial not= :b) [:a :b :c :b :d])] (concat n (rest m)))

который разбивает список на: b, а затем удаляет: b и объединяет два списка.

Ответ 2

Я обычно решаю эти проблемы с помощью функции более высокого порядка, например 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)

Ответ 3

Удивительно, что нет 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)