No Clojure, quando devo usar um vetor sobre uma lista e o contrário?


Respostas:


112

Mais uma vez, parece que respondi à minha própria pergunta, ficando impaciente e fazendo-a no #clojure no Freenode. É bom responder a suas próprias perguntas no Stackoverflow.com: D

Tive uma rápida discussão com Rich Hickey, e aqui está a essência disso.

[12:21] <Raynes>    Vectors aren't seqs, right?
[12:21] <rhickey>   Raynes: no, but they are sequential
[12:21] <rhickey>   ,(sequential? [1 2 3])
[12:21] <clojurebot>    true
[12:22] <Raynes>    When would you want to use a list over a vector?
[12:22] <rhickey>   when generating code, when generating back-to-front
[12:23] <rhickey>   not too often in Clojure

Enquanto você estiver no freenode, venha para o lado sombrio e participe do #stackoverflow! :-P
Chris Jester-Young

Na verdade, eu costumava ficar ocioso lá. Troquei clientes de IRC e nunca pensei em adicionar #stackoverflow à minha lista de ingresso automático.
21909 Rayne

Sou novato no Lisp, mas me perguntei se vetores, mapas e conjuntos quebram de alguma forma a idéia de que todo código é intercambiável com dados? Ou isso é apenas uma daquelas coisas que faz Clojure um Lisp prático? (OR, você pode avaliar um vetor?)
Rob Grant

23
Este é um trecho de bate-papo completamente inútil. "Gerando código" "gerando back-to-front" -> significa precisamente? Estou realmente tendo dificuldades com essa pergunta porque, no meu livro, preguiça + estilo declarativo = desempenho muito melhor, e ainda os vetores são sugeridos em todo o Clojure, o que me deixa totalmente confuso.
Jimmy Hoffa

22
@JimmyHoffa Do jeito que eu entendo: "Gerando código" = "Dentro de uma macro" (porque a maior parte do código é chamada de função, portanto, lista); "gerando de trás para a frente" = "construindo uma sequência anexando".
Omiel

87

Se você já fez muita programação em Java e está familiarizado com a estrutura de coleta Java, pense em listas como LinkedListe vetores como ArrayList. Assim, você pode escolher contêineres da mesma maneira.

Para esclarecimentos adicionais: se você deseja adicionar itens individualmente à frente ou atrás da sequência, uma lista vinculada é muito melhor que um vetor, porque os itens não precisam ser embaralhados a cada vez. No entanto, se você deseja obter elementos específicos (não próximos à frente ou atrás da lista) com frequência (ou seja, acesso aleatório), convém usar o vetor.

A propósito, os vetores podem ser facilmente transformados em seqs.

user=> (def v (vector 1 2 3))
#'user/v
user=> v
[1 2 3]
user=> (seq v)
(1 2 3)
user=> (rseq v)
(3 2 1)

Vetores não são seqs, mas são seqüenciais. (fonte: Rich-se em #clojure on freenode.) Além disso, eu realmente não conheço Java, mas Rich acabou de responder à minha pergunta.
21411 Rayne

1
Vou editar meu post para dizer que os vetores podem ser transformados em seqs, através da função seq. :-) #
31420 Chris Jester-Young

2
Escolha sua resposta porque ela realmente respondeu à pergunta, e eu realmente não gosto de escolher minhas próprias respostas como corretas. Não parece certo. Obrigado. :)
Rayne

Um deque é melhor que uma lista vinculada no caso de adicionar o primeiro e o último. LLs são terríveis: P
encaixotado

1
@boxed Você não pode implementar um deque em cima de um vetor ou ArrayListsem, efetivamente, ArrayDequese reimplementar .
Chris Jester-Young

43

Os vetores têm tempos de acesso aleatório O (1), mas precisam ser pré-alocados. As listas podem ser estendidas dinamicamente, mas o acesso a um elemento aleatório é O (n).


3
Tecnicamente, as listas vinculadas têm tempos de acesso O (1) ... se você estiver acessando apenas o elemento frontal ou traseiro. :-P No entanto, os vetores têm acesso aleatório O (1). :-)
Chris Jester-Young

4
( "Lista Linked", como descrito acima referem-se a listas duplamente ligadas listas Singly ligados tem O (1) o acesso ao elemento frontal única :-P..)
Chris Jester-Young

1
Como alguém que acabou de mergulhar em Clojure, esta é uma resposta MUITO melhor do que as outras duas com mais votos. Os outros dois não me dizem nada de útil.
21814 Keithjgrant

@ ChrisJester-Young A lista vinculada a um único pode suportar acesso O (1) à parte traseira se ela armazenar uma referência ao elemento back, assim .
Gill Bates

30

Quando usar um vetor:

  • Desempenho de acesso indexado - você obtém um custo ~ O (1) para acesso indexado vs. O (n) para listas
  • Anexando - com conj é ~ O (1)
  • Notação conveniente - acho mais fácil digitar e ler [1 2 3] do que '(1 2 3) para obter uma lista literal nas circunstâncias em que qualquer uma delas funcionaria.

Quando usar uma lista:

  • Quando você deseja acessá-lo como uma sequência (uma vez que as listas suportam diretamente a seq sem precisar alocar novos objetos)
  • Anexar - adicionar ao início de uma lista com contras ou, de preferência, conj é O (1)

3
Mesmo ao adicionar / remover nas duas extremidades, uma lista é uma escolha bastante terrível. Um deque é muito melhor (na CPU e principalmente na memória). Tente github.com/pjstadig/deque-clojure
caixa

2
Re: o ~O(1), para aqueles a quem esta explicação custo pode ser útil - stackoverflow.com/questions/200384/constant-amortized-time
Merlyn Morgan-Graham

13

apenas uma observação rápida:

"Eu li que Vetores não são seqs, mas Listas são." 

seqüências são mais genéricas do que listas ou vetores (ou mapas ou conjuntos).
É lamentável que o REPL imprima listas e sequências da mesma forma, porque realmente faz parecer que as listas são sequências, embora sejam diferentes. a função (seq) fará uma sequência de várias coisas diferentes, incluindo listas, e você pode alimentar essa seq com qualquer uma das inúmeras funções que fazem coisas bacanas com as seqs.

user> (class (list 1 2 3))
clojure.lang.PersistentList

user> (class (seq (list 1 2 3)))
clojure.lang.PersistentList

user> (class (seq [1 2 3]))
clojure.lang.PersistentVector$ChunkedSeq

Sec possui um atalho que retorna seu argumento se já é um seq:

user> (let [alist (list 1 2 3)] (identical? alist (seq alist)))
true
user> (identical? (list 1 2 3) (seq (list 1 2 3)))
false

static public ISeq seq(Object coll){
        if(coll instanceof ASeq)
                return (ASeq) coll;
        else if(coll instanceof LazySeq)
                return ((LazySeq) coll).seq();
        else
                return seqFrom(coll);
}

listas são sequências, embora outras coisas também sejam, e nem todas as sequências são listas.


Eu não pretendo escolher um pequeno ponto, é apenas uma oportunidade de apontar algo útil. muitos já vai saber que isso :)
Arthur Ulfeldt

2
Você não quer dizer em classvez de class??
qerub

Não tenho certeza se o seu exemplo mudou após as atualizações de clojure (acho que estou no 1.5), mas os dois exemplos retornam clojure.lang.PersistentListpara mim. Estou assumindo que você pretendia escrever classnão class?.
Adrian Mouat

Eu fiz mesmo! Eu vou consertar isso
Arthur Ulfeldt

Ainda um pouco confuso; Como classretorna o mesmo PersistentList para ambas as expressões mencionadas, isso implica que sequências e listas são exatamente a mesma coisa?
johnbakers
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.