Quel est l'avantage des listes de mots clés?

101

Dans elixir, nous avons des cartes:

> map = %{:a => "one", :b => "two"} # = %{a: "one", b: "two"}
> map.a                             # = "one"
> map[:a]                           # = "one"

Nous avons également des listes de mots clés:

> kl = [a: "one", b: "two"]       # = [a: "one", b: "two"]
> kl2 = [{:a, "one"},{:b, "two"}] # = [a: "one", b: "two"]
> kl == kl2                       # = true
> kl[:a]                          # = "one"
> kl.a                            # = ** (ArgumentError)

Pourquoi les deux?

Syntaxe? Est-ce parce que les listes de mots-clés ont une syntaxe plus flexible permettant de les définir sans boucles et même sans crochets comme dernier paramètre d'un appel de fonction? Alors pourquoi ne pas donner à Maps ce sucre syntaxique?

Dupliquer les clés? Est-ce parce que les listes de mots clés peuvent avoir des clés en double? Pourquoi voudriez-vous à la fois un accès de style carte et des clés en double?

Performance? Est-ce parce que les listes de mots clés ont de meilleures performances? Alors pourquoi avoir des cartes? Et les cartes ne devraient-elles pas être plus performantes pour rechercher les membres par clé qu'une liste de tuples?

JS Array et Ruby Hash aiment l'apparence? Est-ce que c'est ça?

Je comprends que structurellement ce sont des représentations de données différentes. Il me semble que les listes de mots-clés dans elixir servent à compliquer le langage grâce à une syntaxe exceptionnelle (3 variantes syntaxiques différentes), un chevauchement des cas d'utilisation avec des cartes et un avantage peu clair.

Quel est l'avantage d'utiliser les listes de mots clés?

Greggreg
la source

Réponses:

143
                   ┌──────────────┬────────────┬───────────────────────┐
                   │ Keyword List │ Map/Struct │ HashDict (deprecated) │
┌──────────────────┼──────────────┼────────────┼───────────────────────┤
│ Duplicate keys   │ yes          │ no         │ no                    │
│ Ordered          │ yes          │ no         │ no                    │
│ Pattern matching │ yes          │ yes        │ no                    │
│ Performance¹     │ —            │ —          │ —                     │
│ ├ Insert         │ very fast²   │ fast³      │ fast⁴                 │
│ └ Access         │ slow⁵        │ fast³      │ fast⁴                 │
└──────────────────┴──────────────┴────────────┴───────────────────────┘

Les listes de mots clés sont légères et ont une structure simple en dessous, ce qui les rend très flexibles. Vous pouvez les considérer comme du sucre de syntaxe en plus d'une convention Erlang, ce qui facilite l'interface avec Erlang sans écrire de code trop laid. Par exemple, les listes de mots clés sont utilisées pour représenter des arguments de fonction, qui est une propriété héritée d'Erlang. Dans certains cas, les listes de mots clés sont votre seul choix, surtout si vous avez besoin de clés en double ou d'une commande. Ils ont simplement des propriétés différentes des autres alternatives, ce qui les rend plus adaptés à certaines situations et moins à d'autres.

Les cartes (et Structs) sont utilisées pour stocker les données de charge utile réelles, car elles ont une implémentation basée sur le hachage. Les listes de mots-clés en interne ne sont que des listes qui doivent être parcourues pour chaque opération, elles n'ont donc pas les propriétés des structures de données clé-valeur classiques telles que l'accès à temps constant.

Elixir a également été introduit HashDictcomme une solution de contournement pour les mauvaises performances des cartes au moment de leur rédaction . Cependant, cela est corrigé maintenant à partir d'Elixir 1.0.5 / Erlang 18.0 et HashDict sera obsolète dans les versions futures .

Si vous approfondissez la bibliothèque standard d'Erlang, il existe encore plus de structures de données qui stockent des paires clé / valeur:

  • proplists - similaire aux listes de mots clés Elixir
  • cartes - identiques aux cartes Elixir
  • dict - dictionnaires clé-valeur construits à partir de primitives Erlang
  • gb_trees - arbre général équilibré

Vous disposez également de ces options lorsque vous devez stocker des paires clé / valeur sur plusieurs processus et / ou VM:

  • ets / dets - Stockage de termes Erlang (basé sur disque)
  • mnesia - base de données distribuée

¹ De manière générale, mais bien sûr cela dépend ™.

² Le meilleur des cas est juste le préfixe à une liste.

³ S'applique à Elixir 1.0.5 et supérieur, peut être plus lent dans les anciennes versions.

HashDictest désormais obsolète.

⁵ Nécessite une recherche linéaire qui scanne en moyenne la moitié des éléments.

Patrick Oscity
la source
1
Autoriser les clés en double et être commandé ne sont pas des avantages, mais des propriétés différentes. Vous devez choisir la structure de données qui correspond à vos besoins.
droite le
2
À proprement parler, oui, mais ils pourraient s'avérer être des avantages si vous avez besoin de ces propriétés - c'est ce que je voulais dire.
Patrick Oscity
@PatrickOscity: Dans un tel cas, ils seraient sûrement mieux classés comme exigences ?
Courses de légèreté en orbite le
11
@greggreg Il y a un autre avantage implicite à avoir des listes de mots-clés: nous faisons une distinction entre les données structurées et non structurées. Les cartes sont extrêmement utiles pour les données structurées avec un ensemble connu de clés et les mots clés ne le sont pas. Aujourd'hui, la plupart des cartes sont utilisées pour des données structurées et nous laissons des mots-clés pour les optionnels. Si nous n'avions que des cartes, je pense qu'une bonne partie de cette distinction serait perdue.
José Valim
1
En fait, c'est le cas, les cartes sont le chemin à parcourir depuis Erlang 18.
Papipo
12

Le principal avantage des listes de mots clés est une rétrocompatibilité avec les bases de code elixir et erlang existantes.

Ils ajoutent également du sucre de syntaxe s'ils sont utilisés comme arguments de fonctions qui ressemblent par exemple à une syntaxe ruby:

def some_fun(arg, opts \\ []), do: ...
some_fun arg, opt1: 1, opt2: 2

Le principal inconvénient de l'utilisation de listes de mots-clés est qu'il n'est pas possible d'effectuer une correspondance partielle de motif sur celles-ci:

iex(1)> m = %{a: 1, b: 2}
%{a: 1, b: 2}
iex(2)> %{a: a} = m
%{a: 1, b: 2}
iex(3)> a
1
iex(4)> k = [a: 1, b: 2]
[a: 1, b: 2]
iex(5)> [a: a] = k
** (MatchError) no match of right hand side value: [a: 1, b: 2]

Étendons-le aux arguments de fonction. Imaginons que nous devions gérer une fonction multiclause basée sur une valeur de l'une des options:

def fun1(arg, opt1: opt1) when is_nil(opt1), do: do_special_thing
def fun1(arg, opts), do: do_regular_thing

def fun2(arg, %{opt1: opt1}) when is_nil(opt1), do: do_special_thing
def fun2(arg, opts), do: do_regular_thing

Cela n'exécutera jamais do_special_thing:

fun1("arg", opt1: nil, opt2: "some value")
doing regular thing  

Avec des arguments de carte, cela fonctionnera:

fun2("arg", %{opt1: nil, opt2: "some value"})
doing special thing
Voldy
la source
2

Les cartes n'autorisent qu'une seule entrée pour une clé particulière, tandis que les listes de mots clés permettent de répéter la clé. Les cartes sont efficaces (en particulier à mesure qu'elles grandissent) et peuvent être utilisées dans la correspondance de motifs d'Elixir.

En général, utilisez des listes de mots clés pour des éléments tels que les paramètres de ligne de commande et pour transmettre des options, et utilisez des cartes (ou une autre structure de données, le HashDict) lorsque vous voulez un tableau associatif.

Subhash Chandra
la source