¿Qué es el Clojure idiomático para "eliminar" una sola instancia de muchas en una lista?
clojure maps (3)
Tengo una lista, que puede contener elementos que se compararán como iguales. Me gustaría una lista similar, pero con un elemento eliminado. Así que de (: a: b: c: b: d) me gustaría poder "eliminar" solo uno : b para obtener (: a: c: b: d).
El contexto es una mano en un juego de cartas en el que se juegan dos mazos de cartas estándar, por lo que puede haber cartas duplicadas pero aún así se juegan una a la vez.
Tengo código de trabajo, ver más abajo. ¿Hay formas más idiomáticas de hacer esto en 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))
Aquí están las respuestas de Scala que obtuve hace un tiempo: ¿Cuál es una forma idiomática de Scala de "eliminar" un elemento de una lista inmutable?
Es sorprendente que no haya una API de alto nivel para hacer algo como esto. Aquí hay otra versión similar a @amalloy y @James que usa recurrir para no apilar el desbordamiento.
(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)
Normalmente resuelvo estos problemas con una función de orden superior como split-with
, pero alguien ya lo ha hecho. A veces es más legible o más eficiente trabajar a un nivel más primitivo, por lo que aquí hay una mejor versión de su código de bucle original, utilizando secuencias perezosas y generalizadas para tomar un predicado de eliminación en lugar de limitarse a verificaciones de igualdad:
(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)
Qué tal si:
(let [[n m] (split-with (partial not= :b) [:a :b :c :b :d])] (concat n (rest m)))
Lo que divide la lista en: b y luego elimina: b y acumula las dos listas.