2010-09-28 8 views
15

Dada una lista comoExcluyendo elementos duplicados en una lista de cadenas en elisp

(list "foo" "bar" nil "moo" "bar" "moo" nil "affe") 

cómo iba a construir una nueva lista con las cadenas duplicadas eliminadas, así como los nil s despojados, es decir

(list "foo" "bar" "moo" "affe") 

Se debe conservar el orden de los elementos; es posible que la primera aparición de una cadena no se elimine.

Las listas con las que trato aquí son cortas, por lo que no es necesario utilizar nada como una tabla hash para la comprobación de exclusividad, aunque tampoco lo haría. Sin embargo, usar la funcionalidad cl no es una opción viable.

Respuesta

1

Aquí ya go:

(defun strip-duplicates (list) 
    (let ((new-list nil)) 
    (while list 
     (when (and (car list) (not (member (car list) new-list))) 
     (setq new-list (cons (car list) new-list))) 
     (setq list (cdr list))) 
    (nreverse new-list))) 
+0

Esto hace el truco bastante bien. Aprendí sobre 'miembro' ahora. ¿Existe también una función similar que permita especificar una función de comparación? – rafl

+0

No, no que yo sepa. Sin embargo, sería bastante trivial escribir. – Sean

+0

También hay 'memq' y' memql'. – phils

14

El Common Lisp package contiene muchas funciones de manipulación de la lista, en particular, remove-duplicates.

(require 'cl) 
(remove-duplicates (list "foo" "bar" nil "moo" "bar" "moo" nil "affe") 
        :test (lambda (x y) (or (null y) (equal x y))) 
        :from-end t) 

Sí, me doy cuenta de que habías dicho que no desea utilizar cl. Pero sigo mencionando esto como la forma correcta de hacerlo para otras personas que puedan leer este hilo.

(¿Por qué es cl no es viable para usted de todos modos Se ha enviado con Emacs durante unos 20 años, sin contar las encarnaciones pasadas menos destacados?.)

+0

He estado haciendo un par de pequeños parches a Gnus, y en su registro de compromiso veo constantemente cambios reemplazando algo de cl con un equivalente no-cl. No estoy seguro de cuáles son exactamente las razones, pero podría ser algo entre apoyar versiones o sabores extraños de Emacs, o tratar de evitar cargar el paquete 'cl' a menos que sea realmente necesario. – rafl

+2

@rafl Creo que la restricción contra 'cl' comenzó a partir del deseo de RMS de mantener emacs-lisp pequeño. Vea un hilo reciente sobre este punto: http://lists.gnu.org/archive/html/emacs-devel/2010-09/msg01278.html –

+6

@Trey: oh, ya veo. Para mantener el núcleo de Emacs pequeño, se requiere que cada paquete reimplemente sus propias funciones básicas de estructuras de datos. Bueno, si tú eres el que decide que el núcleo de Emacs contendrá exactamente las funciones que usas, funciona. Todos los demás se duplican el esfuerzo y la hinchazón ... – Gilles

28

Trate "Sets and Lists" en el "Lists" section del Emacs Lisp Reference Manual:

(delq nil (delete-dups (list "foo" "bar" nil "moo" "bar" "moo" nil "affe"))) 
+1

Mucho más elegante y directo. A veces realmente deseo que haya espacios de nombres agrupando funciones relacionadas, o quizás un esquema de nombres más consistente que permita buscar algo en particular adivinando su nombre: -/ – rafl

+0

Agrupar (a través de convenciones de nombres o espacios de nombres) es difícil, porque siempre hay muchas maneras de agrupar. Emacs no hace un gran esfuerzo para hacerlo, y en su lugar proporciona cosas como 'apropos'. Pero eso tampoco es ideal. Probablemente deberíamos esforzarnos más, aunque es un poco tarde para corregir a la mayoría de los delincuentes. – Stefan

0

Si utiliza dash.el biblioteca, eso es todo lo que necesita:

(-distinct (-non-nil '(1 1 nil 2 2 nil 3)) ; => (1 2 3) 

dash.el está escrito por Magnar Sveen y es una gran biblioteca de manipulación de listas con muchas funciones para todo tipo de tareas. Recomiendo instalarlo si escribe un montón de código Elisp. La función -distinct quita los elementos duplicados en una lista, -non-nil elimina los elementos nil. Si bien el código anterior es suficiente, a continuación describo una aproximación alternativa, así que no dude en ignorar el resto de la publicación.

-non-nil se añadió en la versión 2.9, por lo que si por alguna razón usted tiene que utilizar versiones anteriores, otra manera de conseguir el mismo es utilizar -keep con una función de identity función, que sólo devuelve lo que se le da: (identity 1) ; => 1. La idea es que -keep guarde solo elementos, para los cuales el predicado devuelve verdadero ("no nulo" en la jerga Lisp). identity obviamente devuelve no-nil solo para los valores que no son nulos:

(-distinct (-keep 'identity '(1 1 nil 2 2 nil 3)) ; => (1 2 3) 
Cuestiones relacionadas