tutorial online framework example clojure

online - Clojure: ¿Cómo reemplazar un elemento en una lista anidada?



clojure web framework (5)

De alguna manera no responde su pregunta, pero si tiene vectores en lugar de listas:

user=> (update-in [1 [2 3] 4 5] [1 1] inc) [1 [2 4] 4 5] user=> (assoc-in [1 [2 3] 4 5] [1 1] 6) [1 [2 6] 4 5]

Entonces, si es posible, evite las listas a favor de vectores para un mejor comportamiento de acceso. Si tiene que trabajar con lazy-seq de varias fuentes, esto por supuesto no es un gran consejo ...

Tengo esta lista profundamente anidada (lista de listas) y quiero reemplazar un solo elemento arbitrario en la lista. Cómo puedo hacer esto ? (El reemplazo incorporado podría reemplazar muchas ocurrencias mientras que necesito reemplazar solo un elemento).


Para los casos simples, una función de sustitución recursiva le dará exactamente lo que necesita sin mucha complejidad adicional. cuando las cosas se vuelven un poco más complejas es hora de abrir y cerrar la creación de clojure en las funciones de cremallera : "Clojure incluye caminar y editar árboles genéricos , usando una técnica llamada cremallera (en el espacio de nombres zip)".

adaptado del ejemplo en: http://clojure.org/other_libraries

(defn randomly-replace [replace-with in-tree] (loop [loc dz] (if (zip/end? loc) (zip/root loc) (recur (zip/next (if (= 0 (get-random-int 10)) (zip/replace loc replace-with) loc)))))

estos funcionarán con algo anidado (seq''able) incluso xmls


Puede usar esta función y adaptarla a sus necesidades (listas anidadas):

(defn replace-item "Returns a list with the n-th item of l replaced by v." [l n v] (concat (take n l) (list v) (drop (inc n) l)))


Una sugerencia ingenua de la galería de maní:

  • copia la lista interna a un vector;
  • toquetee los elementos del vector al azar y al contenido de su corazón usando assoc ;
  • copia el vector a una lista;
  • reemplace la lista anidada en la lista externa.

Esto podría perder algo de rendimiento; pero si se trataba de una operación sensible al rendimiento, en primer lugar estaría trabajando con vectores.


Como ya han dicho todos los demás, usar listas no es una buena idea si necesitas hacer este tipo de cosas. El acceso aleatorio es para lo que están hechos los vectores. assoc-in hace esto de manera eficiente. Con las listas no se puede escapar de recurrir a las sublistas y reemplazar la mayoría de ellas con versiones alteradas de sí mismas hasta llegar a la parte superior.

Sin embargo, este código lo hará, aunque de manera ineficiente y torpe. Préstamo de dermatias:

(defn replace-in-list [coll n x] (concat (take n coll) (list x) (nthnext coll (inc n)))) (defn replace-in-sublist [coll ns x] (if (seq ns) (let [sublist (nth coll (first ns))] (replace-in-list coll (first ns) (replace-in-sublist sublist (rest ns) x))) x))

Uso:

user> (def x ''(0 1 2 (0 1 (0 1 2) 3 4 (0 1 2)))) #''user/x user> (replace-in-sublist x [3 2 0] :foo) (0 1 2 (0 1 (:foo 1 2) 3 4 (0 1 2))) user> (replace-in-sublist x [3 2] :foo) (0 1 2 (0 1 :foo 3 4 (0 1 2))) user> (replace-in-sublist x [3 5 1] ''(:foo :bar)) (0 1 2 (0 1 (0 1 2) 3 4 (0 (:foo :bar) 2)))

Obtendrá IndexOutOfBoundsException si otorga n alguna mayor que la longitud de una sublista. Tampoco es recursivo de la cola. Tampoco es idiomático porque el buen código de Clojure evita usar listas para todo. Es horrible. Probablemente usaría matrices Java mutables antes de usar esto. Creo que entiendes la idea.

Editar

Razones por las cuales las listas son peores que los vectores en este caso:

user> (time (let [x ''(0 1 2 (0 1 (0 1 2) 3 4 (0 1 2)))] ;'' (dotimes [_ 1e6] (replace-in-sublist x [3 2 0] :foo)))) "Elapsed time: 5201.110134 msecs" nil user> (time (let [x [0 1 2 [0 1 [0 1 2] 3 4 [0 1 2]]]] (dotimes [_ 1e6] (assoc-in x [3 2 0] :foo)))) "Elapsed time: 2925.318122 msecs" nil

Tampoco es necesario que escriba una assoc-in , ya existe. Mire la implementación de assoc-in alguna vez; es simple y directo (en comparación con la versión de la lista) gracias a los vectores que brindan acceso aleatorio eficiente y fácil por índice, a través de get .

Tampoco tiene que citar vectores como si tuviera que citar listas. Las listas en Clojure implican fuertemente "Estoy llamando a una función o macro aquí".

Los vectores (y mapas, conjuntos, etc.) se pueden atravesar a través de seq s. Puede usar vectores de forma transparente en forma de lista, entonces ¿por qué no usar vectores y tener lo mejor de ambos mundos?

Los vectores también se destacan visualmente. El código de Clojure es menos de una gran cantidad de parens que otros Lisps gracias al uso generalizado de [] y {} . Algunas personas lo encuentran molesto, creo que hace que las cosas sean más fáciles de leer. (La sintaxis de mi editor resalta () , [] y {} diferente, lo que ayuda aún más).

Algunas instancias usaría una lista de datos:

  1. Si tengo una estructura de datos ordenada que necesita crecer desde el principio, nunca necesitaré acceso aleatorio a
  2. Construyendo un seq "a mano", como via lazy-seq
  3. Escribir una macro, que necesita devolver el código como datos