Qual é a melhor maneira de testar se uma lista contém um determinado valor no Clojure?
Em particular, o comportamento de contains?
está atualmente me confundindo:
(contains? '(100 101 102) 101) => false
Obviamente, eu poderia escrever uma função simples para percorrer a lista e testar a igualdade, mas certamente deve haver uma maneira padrão de fazer isso?
data-structures
clojure
Mikera
fonte
fonte
Respostas:
Ah,
contains?
... supostamente uma das cinco principais perguntas frequentes sobre: Clojure.Ele não verifica se uma coleção contém um valor; verifica se um item pode ser recuperado com
get
ou, em outras palavras, se uma coleção contém uma chave. Isso faz sentido para conjuntos (que podem ser considerados como não fazendo distinção entre chaves e valores), mapas (como(contains? {:foo 1} :foo)
étrue
) e vetores (mas note que(contains? [:foo :bar] 0)
étrue
porque as chaves aqui são índices e o vetor em questão "contém" o índice0
!).Para aumentar a confusão, nos casos em que não faz sentido ligarAtualização: No Clojure,contains?
, basta retornarfalse
; é isso que acontece(contains? :foo 1)
e também(contains? '(100 101 102) 101)
.contains?
lançamentos ≥ 1,5 quando entregue um objeto de um tipo que não suporta o teste de "associação de chave" pretendido.A maneira correta de fazer o que você está tentando fazer é a seguinte:
Ao procurar um de vários itens, você pode usar um conjunto maior; Ao procurar por
false
/nil
, você pode usarfalse?
/nil?
- porque(#{x} x)
retornax
, assim(#{nil} nil)
énil
; ao procurar um dos vários itens, alguns dos quais podem serfalse
ounil
, você pode usar(Observe que os itens podem ser passados para
zipmap
qualquer tipo de coleção.)fonte
(some #{101} '(100 101 102))
dizer que "na maioria das vezes isso funciona". Não é justo dizer que sempre funciona? Estou usando o Clojure 1.4 e a documentação usa esse tipo de exemplo. Funciona para mim e faz sentido. Existe algum tipo de caso especial em que não funciona?false
ounil
- veja o parágrafo a seguir. Em uma observação separada, no Clojure 1.5-RC1contains?
lança uma exceção quando recebe uma coleção sem chave como argumento. Suponho que editarei esta resposta quando o lançamento final for lançado.Aqui está o meu utilitário padrão para o mesmo objetivo:
fonte
nil
efalse
. Agora, por que isso não faz parte do clojure / core?seq
poderia ser renomeado paracoll
, para evitar confusão com a funçãoseq
?seq
dentro do corpo, não há conflito com o parâmetro com o mesmo nome. Mas fique à vontade para editar a resposta se você acha que a renomeação facilitaria o entendimento.(boolean (some #{elm} coll))
se você não precisar se preocupar comnil
oufalse
.Você sempre pode chamar métodos java com a sintaxe .methodName.
fonte
contains?
, Qc Na bateu nele com um Bô e disse: "Aluno estúpido! Você precisa perceber que não há colher. É tudo apenas Java por baixo! Use a notação de ponto". Nesse momento, Anton ficou iluminado.Sei que estou um pouco atrasado, mas e quanto a:
Finalmente, no clojure 1.4, as saídas são verdadeiras :)
fonte
(set '(101 102 103))
é o mesmo que%{101 102 103}
. Portanto, sua resposta pode ser escrita como(contains? #{101 102 103} 102)
.'(101 102 103)
em um conjunto.Funciona, mas abaixo é melhor:
fonte
Pelo que vale a pena, esta é minha implementação simples de uma função contém para listas:
fonte
(defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
Se você tem um vetor ou lista e deseja verificar se um valor está contido nele, verá que
contains?
isso não funciona. Michał já explicou o porquê .Há quatro coisas que você pode tentar neste caso:
Considere se você realmente precisa de um vetor ou lista. Se você usar um conjunto ,
contains?
funcionará.Use
some
, agrupando o destino em um conjunto, da seguinte maneira:O atalho de configuração como função não funcionará se você estiver procurando por um valor falso (
false
ounil
).Nesses casos, você deve usar a função de predicado interno para esse valor
false?
ounil?
:Se você precisar fazer muito esse tipo de pesquisa, escreva uma função para ele :
Além disso, consulte a resposta de Michał para obter formas de verificar se algum dos vários destinos está contido em uma sequência.
fonte
Aqui está uma função rápida dos meus utilitários padrão que eu uso para esse fim:
fonte
Aqui está a solução clássica do Lisp:
fonte
some
é potencialmente paralelo entre os núcleos disponíveis.Eu desenvolvi a versão jg-faustus de "list-contains?". Agora é preciso qualquer número de argumentos.
fonte
É tão simples quanto usar um conjunto - semelhante aos mapas, você pode simplesmente deixá-lo na posição de função. Ele avalia o valor se no conjunto (que é verdade) ou
nil
(que é falsey):Se você estiver verificando um vetor / lista de tamanho razoável que não terá até o tempo de execução, também poderá usar a
set
função:fonte
A maneira recomendada é usar
some
com um conjunto - consulte a documentação paraclojure.core/some
.Você pode usar
some
dentro de um predicado verdadeiro / falso real, por exemplofonte
if
true
efalse
?some
já retorna valores true-ish e false-ish.fonte
exemplo de uso (qual? [1 2 3] 3) ou (qual? # {1 2 3} 4 5 3)
fonte
Como o Clojure é construído em Java, você também pode chamar facilmente a
.indexOf
função Java. Essa função retorna o índice de qualquer elemento em uma coleção e, se não conseguir encontrar esse elemento, retorna -1.Fazendo uso disso, poderíamos simplesmente dizer:
fonte
O problema com a solução 'recomendada' é que ela quebra quando o valor que você procura é 'nulo'. Eu prefiro esta solução:
fonte
Existem funções convenientes para esse fim na biblioteca Tupelo . Em particular, as funções
contains-elem?
,contains-key?
econtains-val?
são muito úteis. A documentação completa está presente nos documentos da API .contains-elem?
é o mais genérico e destina-se a vetores ou qualquer outro clojureseq
:Aqui vemos que, para um intervalo inteiro ou um vetor misto,
contains-elem?
funciona como esperado para elementos existentes e inexistentes na coleção. Para mapas, também podemos procurar qualquer par de valores-chave (expresso como um vetor len-2):Também é simples pesquisar um conjunto:
Para mapas e conjuntos, é mais simples (e mais eficiente) usar
contains-key?
para encontrar uma entrada de mapa ou um elemento de conjunto:E, para mapas, você também pode procurar valores com
contains-val?
:Como visto no teste, cada uma dessas funções funciona corretamente ao pesquisar
nil
valores.fonte
Outra opção:
Use java.util.Collection # contains ():
fonte