Что такое идиоматическое Clojure для «удаления» одного экземпляра из множества в списке? - PullRequest
16 голосов
/ 05 октября 2011

У меня есть список, который может содержать элементы, которые будут сравниваться как равные.Я хотел бы подобный список, но с одним удаленным элементом.Итак, из (: a: b: c: b: d) я хотел бы иметь возможность "удалить" только one : 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 «удалить» один элемент из неизменяемого Списка

Ответы [ 3 ]

24 голосов
/ 05 октября 2011

Как насчет:

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

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

9 голосов
/ 05 октября 2011

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

Удивительно, что нет высокоуровневого 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...