¿Cuál es la mejor manera de probar si una lista contiene un valor dado en Clojure?
En particular, el comportamiento de contains?
me está confundiendo actualmente:
(contains? '(100 101 102) 101) => false
Obviamente, podría escribir una función simple para recorrer la lista y probar la igualdad, pero seguramente debe haber una forma estándar de hacerlo.
data-structures
clojure
mikera
fuente
fuente
Respuestas:
Ah,
contains?
... supuestamente una de las cinco preguntas más frecuentes sobre re: Clojure.No , no comprueba si una colección contiene un valor; comprueba si un elemento se puede recuperar con
get
o, en otras palabras, si una colección contiene una clave. Esto tiene sentido para los conjuntos (que puede ser pensado como no haciendo distinción entre claves y valores), mapas (por lo que(contains? {:foo 1} :foo)
estrue
) y vectores (pero nota que(contains? [:foo :bar] 0)
estrue
, porque las claves aquí son índices y el vector en cuestión no "contienen" la índice0
!).Para agregar a la confusión, en los casos en que no tiene sentido llamarActualización: en Clojure ≥ 1.5contains?
, simplemente regresafalse
; Esto es lo que sucede en(contains? :foo 1)
y también(contains? '(100 101 102) 101)
.contains?
tiros cuando se entrega un objeto de un tipo que no admite la prueba de "membresía clave" prevista.La forma correcta de hacer lo que intenta hacer es la siguiente:
Al buscar uno de los muchos elementos, puede usar un conjunto más grande; cuando busque
false
/nil
, puede usarfalse?
/nil?
- porque(#{x} x)
devuelvex
, por lo tanto(#{nil} nil)
esnil
; al buscar uno de los múltiples elementos, algunos de los cuales pueden serfalse
onil
, puede usar(Tenga en cuenta que los elementos se pueden pasar a
zipmap
cualquier tipo de colección).fuente
(some #{101} '(100 101 102))
decir que "la mayoría de las veces esto funciona". ¿No es justo decir que siempre funciona? Estoy usando Clojure 1.4 y la documentación usa este tipo de ejemplo. Funciona para mi y tiene sentido. ¿Hay algún tipo de caso especial donde no funciona?false
onil
- vea el siguiente párrafo. En una nota separada, en Clojure 1.5-RC1contains?
arroja una excepción cuando se le da una colección sin clave como argumento. Supongo que editaré esta respuesta cuando salga la versión final.Aquí está mi utilidad estándar para el mismo propósito:
fuente
nil
yfalse
. Ahora, ¿por qué esto no es parte de clojure / core?seq
¿podría cambiar el nombre acoll
, para evitar confusiones con la funciónseq
?seq
dentro del cuerpo, no hay conflicto con el parámetro del mismo nombre. Pero siéntase libre de editar la respuesta si cree que el cambio de nombre facilitaría la comprensión.(boolean (some #{elm} coll))
si no tiene que preocuparsenil
ofalse
.Siempre puede llamar a métodos java con la sintaxis .methodName.
fuente
contains?
, Qc Na lo golpeó con un Bô y dijo: "¡Estúpido estudiante! Debes darte cuenta de que no hay cuchara. ¡ Todo es solo Java debajo! Usa la notación de puntos". En ese momento, Anton se iluminó.Sé que llego un poco tarde, pero ¿qué pasa con:
Por fin en clojure 1.4 salidas true :)
fuente
(set '(101 102 103))
es el mismo que%{101 102 103}
. Entonces su respuesta se puede escribir como(contains? #{101 102 103} 102)
.'(101 102 103)
a un conjunto.Funciona, pero a continuación es mejor:
fuente
Por lo que vale, esta es mi implementación simple de una función contiene para listas:
fuente
(defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
Si tiene un vector o una lista y desea verificar si un valor está contenido en él, encontrará que
contains?
no funciona. Michał ya ha explicado por qué .Hay cuatro cosas que puedes probar en este caso:
Considere si realmente necesita un vector o una lista. Si utiliza un conjunto en su lugar ,
contains?
funcionará.Use
some
, envolviendo el objetivo en un conjunto, de la siguiente manera:El acceso directo de establecer como función no funcionará si está buscando un valor falso (
false
onil
).En estos casos, debe usar la función de predicado incorporada para ese valor,
false?
onil?
:Si necesita hacer mucho este tipo de búsqueda, escriba una función para ello :
Además, vea la respuesta de Michał para ver formas de verificar si alguno de los múltiples objetivos están contenidos en una secuencia.
fuente
Aquí hay una función rápida de mis utilidades estándar que uso para este propósito:
fuente
Aquí está la solución clásica de Lisp:
fuente
some
es potencialmente paralelo en los núcleos disponibles.Me basé en la versión jg-faustus de "list-contiene?". Ahora toma cualquier número de argumentos.
fuente
Es tan simple como usar un conjunto: similar a los mapas, simplemente puede colocarlo en la posición de la función. Evalúa el valor si está en el conjunto (que es verdadero) o
nil
(que es falsey):Si está comparando con un vector / lista de tamaño razonable que no tendrá hasta el tiempo de ejecución, también puede usar la
set
función:fuente
La forma recomendada es utilizarlo
some
con un conjunto; consulte la documentación paraclojure.core/some
.A continuación, puede usar
some
dentro de un predicado verdadero verdadero / falso, por ejemplofuente
if
true
yfalse
?some
ya devuelve valores true-ish y false-ish.fuente
ejemplo de uso (¿cuál? [1 2 3] 3) o (cuál? # {1 2 3} 4 5 3)
fuente
Como Clojure está construido en Java, puede llamar fácilmente al
.indexOf
función Java. Esta función devuelve el índice de cualquier elemento en una colección, y si no puede encontrar este elemento, devuelve -1.Haciendo uso de esto, simplemente podríamos decir:
fuente
El problema con la solución 'recomendada' es que se rompe cuando el valor que busca es 'nulo'. Prefiero esta solución:
fuente
Hay funciones convenientes para este propósito en la biblioteca Tupelo . En particular, las funciones
contains-elem?
,contains-key?
ycontains-val?
son muy útiles. La documentación completa está presente en los documentos de la API .contains-elem?
es el más genérico y está destinado a vectores o cualquier otra clojureseq
:Aquí vemos que para un rango entero o un vector mixto,
contains-elem?
funciona como se espera para los elementos existentes y no existentes en la colección. Para los mapas, también podemos buscar cualquier par clave-valor (expresado como un vector len-2):También es sencillo buscar un conjunto:
Para mapas y conjuntos, es más simple (y más eficiente) usarlo
contains-key?
para encontrar una entrada de mapa o un elemento de conjunto:Y, para los mapas, también puede buscar valores con
contains-val?
:Como se ve en la prueba, cada una de estas funciones funciona correctamente cuando se buscan
nil
valores.fuente
Otra opción:
Use java.util.Collection # contiene ():
fuente