Qual è il vantaggio degli elenchi di parole chiave?


101

In elisir abbiamo Maps:

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

Abbiamo anche elenchi di parole chiave:

> 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)

Perché entrambi?

Sintassi? È perché gli elenchi di parole chiave hanno una sintassi più flessibile che consente loro di essere definiti senza riccioli e anche senza parentesi come l'ultimo parametro di una chiamata di funzione? Allora perché non dare a Maps questo zucchero sintattico?

Chiavi duplicate? È perché gli elenchi di parole chiave possono avere chiavi duplicate? Perché vorresti sia l'accesso in stile mappa che le chiavi duplicate?

Prestazione? È perché gli elenchi di parole chiave hanno prestazioni migliori? Allora perché hanno Maps? E le mappe non dovrebbero essere più performanti nel cercare i membri per chiave rispetto a un elenco di tuple?

JS Array e Ruby Hash come l'aspetto? È così?

Capisco che strutturalmente sono rappresentazioni di dati differenti. A me sembra che gli elenchi di parole chiave in elisir servano a complicare il linguaggio attraverso una sintassi eccezionale (3 diverse varianti sintattiche), la sovrapposizione dei casi d'uso con le mappe e un vantaggio poco chiaro.

Qual è il vantaggio dell'utilizzo degli elenchi di parole chiave?

Risposte:


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⁴                 │
└──────────────────┴──────────────┴────────────┴───────────────────────┘

Gli elenchi di parole chiave sono leggeri e hanno una struttura semplice al di sotto, che li rende molto flessibili. Puoi pensarli come uno zucchero di sintassi in cima a una convenzione di Erlang, rendendo facile l'interfacciamento con Erlang senza scrivere codice troppo brutto. Ad esempio, gli elenchi di parole chiave vengono utilizzati per rappresentare gli argomenti della funzione, che è una proprietà ereditata da Erlang. In alcuni casi, gli elenchi di parole chiave sono la tua unica scelta, soprattutto se hai bisogno di chiavi duplicate o ordini. Hanno semplicemente proprietà diverse rispetto alle altre alternative, che le rendono più adatte per alcune situazioni e meno per altre.

Le mappe (e le strutture) vengono utilizzate per archiviare i dati del carico utile effettivo, poiché hanno un'implementazione basata su hash. Gli elenchi di parole chiave internamente sono solo elenchi che devono essere attraversati per ciascuna operazione, quindi non hanno le proprietà delle strutture di dati chiave-valore classiche come l'accesso nel tempo costante.

Elixir è stato introdotto anche HashDictcome soluzione alternativa alle scarse prestazioni delle mappe nel momento in cui sono state scritte . Tuttavia, questo problema è stato risolto ora a partire da Elixir 1.0.5 / Erlang 18.0 e HashDict sarà deprecato nelle versioni future .

Se approfondisci la libreria standard di Erlang, ci sono ancora più strutture dati che memorizzano coppie chiave / valore:

  • proplists - simile agli elenchi di parole chiave Elixir
  • mappe - come le mappe Elisir
  • dict - dizionari di valori-chiave costruiti dalle primitive di Erlang
  • gb_trees - albero bilanciato generale

Hai anche queste opzioni quando devi memorizzare coppie chiave / valore su più processi e / o VM:

  • ets / dets - (basato su disco) archiviazione a termine Erlang
  • mnesia - database distribuito

¹ In generale, ma ovviamente dipende ™.

² Il caso migliore è solo l'anteprima di un elenco.

³ Si applica a Elixir 1.0.5 e versioni successive, potrebbe essere più lento nelle versioni precedenti.

HashDictè ora deprecato.

⁵ Richiede una ricerca lineare che mediamente scansiona la metà degli elementi.


1
Consentire chiavi duplicate ed essere ordinati non sono vantaggi, ma proprietà diverse. Devi scegliere la struttura dei dati che si adatta alle tue esigenze.
destra

2
A rigor di termini, sì, ma potrebbero rivelarsi vantaggi se hai bisogno di quelle proprietà - questo è ciò che intendevo.
Patrick Oscity

@PatrickOscity: In tal caso, sicuramente sarebbero meglio classificati come requisiti ?
Gare di leggerezza in orbita

11
@greggreg C'è un altro vantaggio implicito nell'avere elenchi di parole chiave: facciamo una distinzione tra dati strutturati e non strutturati. Le mappe sono estremamente utili per i dati strutturati con un set noto di chiavi e le parole chiave non lo sono. Oggi, la maggior parte dell'utilizzo delle mappe riguarda i dati strutturati e lasciamo le parole chiave per quelle facoltative. Se avessimo solo le mappe penso che una buona parte di questa distinzione andrebbe persa.
José Valim,

1
In effetti lo ha fatto, le mappe sono la strada da percorrere da erlang 18.
Papipo

12

Il vantaggio principale degli elenchi di parole chiave è la compatibilità con le versioni precedenti di elixir e base di codice erlang esistenti.

Aggiungono anche lo zucchero di sintassi se usati come argomenti di funzioni che assomigliano ad esempio a una sintassi di ruby:

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

Lo svantaggio principale dell'utilizzo degli elenchi di parole chiave è che non è possibile eseguire una corrispondenza parziale del pattern su di essi:

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]

Estendiamolo agli argomenti della funzione. Immagina di dover gestire una funzione multiclause basata su un valore di una delle opzioni:

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

Questo non eseguirà mai do_special_thing:

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

Con gli argomenti della mappa funzionerà:

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

2

Le mappe consentono solo una voce per una chiave particolare, mentre gli elenchi di parole chiave consentono di ripetere la chiave. Le mappe sono efficienti (in particolare man mano che crescono) e possono essere utilizzate nella corrispondenza dei modelli di Elixir.

In generale, usa elenchi di parole chiave per cose come parametri della riga di comando e per passare opzioni e usa mappe (o un'altra struttura di dati, HashDict) quando vuoi un array associativo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.