A maioria das linguagens funcionais usa listas vinculadas como sua principal estrutura de dados imutáveis. Por que listas, e não por exemplo, árvores? As árvores também podem reutilizar caminhos e até listas de modelos.
A maioria das linguagens funcionais usa listas vinculadas como sua principal estrutura de dados imutáveis. Por que listas, e não por exemplo, árvores? As árvores também podem reutilizar caminhos e até listas de modelos.
Respostas:
Porque listas são mais simples que árvores. (Você pode ver isso trivialmente pelo fato de uma lista ser uma árvore degenerada, onde cada nó tem apenas um único filho.)
A lista de contras é a estrutura de dados recursiva mais simples possível, de tamanho arbitrário.
Guy Steele argumentou durante o projeto da linguagem de programação Fortress que, para os cálculos massivamente paralelos do futuro, nossas estruturas de dados e nosso fluxo de controle devem ter a forma de uma árvore com vários ramos, não lineares como são agora. Mas, por enquanto, a maioria das nossas bibliotecas principais de estrutura de dados foi projetada com processamento sequencial e iterativo (ou recursão de cauda, isso realmente não importa, são a mesma coisa) em mente, não com processamento paralelo.
Observe que, por exemplo, em Clojure, cujas estruturas de dados foram projetadas especificamente para o mundo paralelo, distribuído e "nublado" de hoje, mesmo matrizes (chamadas vetores em Clojure), provavelmente a estrutura de dados mais "linear" de todas, são realmente implementadas como árvores
Portanto, resumindo: uma lista de contras é a estrutura de dados recursiva persistente mais simples possível e não foi necessário escolher um "padrão" mais complicado. Outros estão obviamente disponíveis como opções, por exemplo, Haskell possui matrizes, filas prioritárias, mapas, pilhas, treaps, tentativas e tudo o que você possa imaginar, mas o padrão é a lista de contras simples.
data Tree a = Leaf a | Branch a (Tree a) (Tree a)
. Isso reforça seu argumento de "simplicidade".
Sequence
ou Scala Vector
), mas não as usam quando precisam apenas de ler, pois podem conseguir isso em tempo constante (por exemplo, de Haskell Vector
ou F # através da Net ImmutableArray
)
pmap
ping sobre um vetor no Clojure ainda acessa cada elemento sequencialmente; a estrutura em árvore do vetor geralmente está oculta do usuário final.
Na verdade, essas listas são árvores! Você tem nós com dois campos car
e cdr
, que podem conter mais desses nós ou folhas. A única coisa que transforma essas árvores em listas é a convenção de interpretar o cdr
link como um link para o próximo nó em uma lista linear e o car
link como o valor do nó atual.
Dito isso, acho que a prevalência de listas vinculadas na programação funcional está ligada à prevalência de recursão sobre a iteração. Quando sua única construção em loop disponível é a recursão (final), você deseja estruturas de dados que sejam fáceis de usar com isso; e listas vinculadas são perfeitas para isso.