Quelle est la meilleure façon de tester si une liste contient une valeur donnée dans Clojure?
En particulier, le comportement de contains?
me déroute actuellement:
(contains? '(100 101 102) 101) => false
Je pourrais évidemment écrire une fonction simple pour parcourir la liste et tester l'égalité, mais il doit sûrement y avoir un moyen standard de le faire?
data-structures
clojure
Mikera
la source
la source
Réponses:
Ah,
contains?
... soi-disant l'une des cinq principales FAQ concernant Clojure.Il ne vérifie pas si une collection contient une valeur; il vérifie si un élément peut être récupéré avec
get
ou, en d'autres termes, si une collection contient une clé. Cela a du sens pour les ensembles (qui peuvent être considérés comme ne faisant aucune distinction entre les clés et les valeurs), les cartes (ainsi(contains? {:foo 1} :foo)
esttrue
) et les vecteurs (mais notez que(contains? [:foo :bar] 0)
c'esttrue
, parce que les clés ici sont des indices et le vecteur en question "contient" le index0
!).Pour ajouter à la confusion, dans les cas où cela n'a pas de sens d'appelerMise à jour: Dans Clojure ≥ 1.5contains?
, il revient simplementfalse
; c'est ce qui se passe dans(contains? :foo 1)
et aussi(contains? '(100 101 102) 101)
.contains?
lance quand on lui donne un objet d'un type qui ne prend pas en charge le test "d'appartenance clé" prévu.La bonne façon de faire ce que vous essayez de faire est la suivante:
Lorsque vous recherchez l'un des nombreux éléments, vous pouvez utiliser un ensemble plus grand; lors de la recherche
false
/nil
, vous pouvez utiliserfalse?
/nil?
- parce que les(#{x} x)
rendementsx
, ce qui(#{nil} nil)
estnil
; lors de la recherche d'un élément parmi plusieurs, dont certains peuvent êtrefalse
ounil
, vous pouvez utiliser(Notez que les éléments peuvent être transmis
zipmap
dans n'importe quel type de collection.)la source
(some #{101} '(100 101 102))
disant que "la plupart du temps cela fonctionne". N'est-il pas juste de dire que cela fonctionne toujours? J'utilise Clojure 1.4 et la documentation utilise ce genre d'exemple. Cela fonctionne pour moi et a du sens. Y a-t-il une sorte de cas particulier où cela ne fonctionne pas?false
ounil
- voir le paragraphe suivant. Sur une note distincte, dans Clojure 1.5-RC1contains?
lève une exception lorsqu'on lui donne une collection sans clé comme argument. Je suppose que je modifierai cette réponse lorsque la version finale sortira.Voici mon utilitaire standard dans le même but:
la source
nil
etfalse
. Maintenant, pourquoi ne fait-il pas partie de clojure / core?seq
pourrait peut-être être renommé encoll
, pour éviter toute confusion avec la fonctionseq
?seq
à l'intérieur du corps, il n'y a pas de conflit avec le paramètre du même nom. Mais n'hésitez pas à modifier la réponse si vous pensez que le changement de nom faciliterait la compréhension.(boolean (some #{elm} coll))
si vous n'avez pas à vous soucier denil
oufalse
.Vous pouvez toujours appeler des méthodes java avec la syntaxe .methodName.
la source
contains?
, Qc Na l'a frappé avec un Bô et a dit: "Stupide étudiant! Vous devez réaliser qu'il n'y a pas de cuillère. C'est juste Java en dessous! Utilisez la notation par points.". À ce moment-là, Anton s'est éclairé.Je sais que je suis un peu en retard, mais qu'en est-il:
Enfin dans clojure 1.4 sort vrai :)
la source
(set '(101 102 103))
est le même que%{101 102 103}
. Donc, votre réponse peut être écrite comme(contains? #{101 102 103} 102)
.'(101 102 103)
en un ensemble.Fonctionne, mais ci-dessous est mieux:
la source
Pour ce que cela vaut, voici ma simple implémentation d'une fonction contient pour les listes:
la source
(defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
Si vous avez un vecteur ou une liste et que vous souhaitez vérifier si une valeur y est contenue, vous constaterez que
contains?
cela ne fonctionne pas. Michał a déjà expliqué pourquoi .Il y a quatre choses que vous pouvez essayer dans ce cas:
Demandez-vous si vous avez vraiment besoin d'un vecteur ou d'une liste. Si vous utilisez un ensemble à la place ,
contains?
cela fonctionnera.Utilisez
some
, enveloppant la cible dans un ensemble, comme suit:Le raccourci définir en tant que fonction ne fonctionnera pas si vous recherchez une valeur erronée (
false
ounil
).Dans ces cas, vous devez utiliser la fonction de prédicat intégrée pour cette valeur,
false?
ounil?
:Si vous devez souvent effectuer ce type de recherche, écrivez une fonction pour cela :
Voir également la réponse de Michał pour savoir si l'une des cibles multiples est contenue dans une séquence.
la source
Voici une fonction rapide de mes utilitaires standard que j'utilise à cet effet:
la source
Voici la solution Lisp classique:
la source
some
est potentiellement parallèle entre les cœurs disponibles.J'ai construit sur la version jg-faustus de "list-contains?". Il prend maintenant n'importe quel nombre d'arguments.
la source
C'est aussi simple que d'utiliser un ensemble - semblable aux cartes, vous pouvez simplement le déposer dans la position de fonction. Il évalue à la valeur si dans l'ensemble (qui est vrai) ou
nil
(qui est faux):Si vous comparez un vecteur / une liste de taille raisonnable que vous n'aurez pas avant l'exécution, vous pouvez également utiliser la
set
fonction:la source
La méthode recommandée est de l'utiliser
some
avec un ensemble - voir la documentation pourclojure.core/some
.Vous pouvez alors utiliser
some
dans un vrai prédicat vrai / faux, par exemplela source
if
true
etfalse
?some
renvoie déjà des valeurs true-ish et false-ish.la source
exemple d'utilisation (laquelle? [1 2 3] 3) ou (laquelle? # {1 2 3} 4 5 3)
la source
Puisque Clojure est construit sur Java, vous pouvez tout aussi facilement appeler la
.indexOf
fonction Java. Cette fonction renvoie l'index de tout élément d'une collection et, si elle ne trouve pas cet élément, renvoie -1.En utilisant cela, nous pourrions simplement dire:
la source
Le problème avec la solution «recommandée» est qu'elle est interrompue lorsque la valeur que vous recherchez est «nulle». Je préfère cette solution:
la source
Il existe des fonctions pratiques à cet effet dans la bibliothèque Tupelo . En particulier, les fonctions
contains-elem?
,contains-key?
etcontains-val?
sont très utiles. Une documentation complète est présente dans la documentation de l'API .contains-elem?
est le plus générique et est destiné aux vecteurs ou à tout autre clojureseq
:Ici, nous voyons que pour une plage d'entiers ou un vecteur mixte,
contains-elem?
fonctionne comme prévu pour les éléments existants et inexistants dans la collection. Pour les cartes, nous pouvons également rechercher n'importe quelle paire clé-valeur (exprimée sous forme de vecteur len-2):Il est également simple de rechercher un ensemble:
Pour les cartes et les ensembles, il est plus simple (et plus efficace) d'utiliser
contains-key?
pour trouver une entrée de carte ou un élément d'ensemble:Et, pour les cartes, vous pouvez également rechercher des valeurs avec
contains-val?
:Comme vu dans le test, chacune de ces fonctions fonctionne correctement lors de la recherche de
nil
valeurs.la source
Une autre option:
Utilisez java.util.Collection # contains ():
la source