Was ist der Vorteil von Keyword-Listen?

101

In Elixier haben wir Karten:

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

Wir haben auch Keyword-Listen:

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

Warum beides?

Syntax? Liegt es daran, dass Keyword-Listen eine flexiblere Syntax haben, mit der sie ohne Curlys und sogar ohne Klammern als letzter Parameter eines Funktionsaufrufs definiert werden können? Warum geben Sie Maps dann nicht diesen syntaktischen Zucker?

Doppelte Schlüssel? Liegt es daran, dass Keyword-Listen doppelte Schlüssel haben können? Warum sollten Sie sowohl Zugriff im Kartenstil als auch doppelte Schlüssel wünschen?

Performance? Liegt es daran, dass Keyword-Listen eine bessere Leistung haben? Warum dann Karten? Und sollten Karten nicht leistungsfähiger sein, wenn es darum geht, Mitglieder nach Schlüssel zu suchen, als eine Liste von Tupeln?

JS Array und Ruby Hash mögen Aussehen? Ist es das?

Ich verstehe, dass es sich strukturell um unterschiedliche Datendarstellungen handelt. Mir scheint, dass Keyword-Listen in Elixier dazu dienen, die Sprache durch außergewöhnliche Syntax (3 verschiedene syntaktische Varianten), Überschneidungen von Anwendungsfällen mit Karten und einen unklaren Vorteil zu komplizieren.

Was ist der Vorteil der Verwendung von Keyword-Listen?

greggreg
quelle

Antworten:

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

Keyword-Listen sind leichtgewichtig und haben eine einfache Struktur, wodurch sie sehr flexibel sind. Sie können sie als Syntaxzucker über einer Erlang-Konvention betrachten, wodurch es einfach wird, mit Erlang zu kommunizieren, ohne zu hässlichen Code zu schreiben. Beispielsweise werden Schlüsselwortlisten verwendet, um Funktionsargumente darzustellen, eine von Erlang geerbte Eigenschaft. In einigen Fällen sind Schlüsselwortlisten Ihre einzige Wahl, insbesondere wenn Sie doppelte Schlüssel oder Bestellungen benötigen. Sie haben einfach andere Eigenschaften als die anderen Alternativen, wodurch sie für bestimmte Situationen besser und für andere weniger geeignet sind.

Maps (und Structs) werden zum Speichern der tatsächlichen Nutzdaten verwendet, da sie eine Hash-basierte Implementierung haben. Intern sind Schlüsselwortlisten nur Listen, die für jede Operation durchlaufen werden müssen, sodass sie nicht die Eigenschaften klassischer Schlüsselwertdatenstrukturen wie konstanten Zeitzugriff aufweisen.

Elixir wurde auch HashDictals Problemumgehung für die schlechte Leistung von Karten zum Zeitpunkt der Erstellung eingeführt . Dies ist jedoch ab Elixir 1.0.5 / Erlang 18.0 behoben und HashDict wird in zukünftigen Versionen nicht mehr unterstützt .

Wenn Sie tiefer in die Erlang-Standardbibliothek eintauchen, gibt es noch mehr Datenstrukturen, in denen Schlüssel / Wert-Paare gespeichert sind:

  • proplists - ähnlich wie Elixir-Keyword-Listen
  • Karten - wie Elixierkarten
  • dict - Schlüsselwertwörterbücher, die aus Erlang-Grundelementen erstellt wurden
  • gb_trees - allgemeiner ausgeglichener Baum

Sie haben auch diese Optionen, wenn Sie Schlüssel / Wert-Paare über mehrere Prozesse und / oder VMs hinweg speichern müssen:

  • ets / dets - (festplattenbasiert) Erlang Term Storage
  • mnesia - verteilte Datenbank

¹ Im Allgemeinen, aber natürlich kommt es darauf an ™.

² Im besten Fall wird nur eine Liste vorangestellt.

³ Gilt für Elixir 1.0.5 und höher, kann in älteren Versionen langsamer sein.

HashDictwird jetzt veraltet.

⁵ Erfordert eine lineare Suche, bei der durchschnittlich die Hälfte der Elemente gescannt wird.

Patrick Oscity
quelle
1
Das Zulassen doppelter Schlüssel und das Bestellen sind keine Vorteile, sondern unterschiedliche Eigenschaften. Sie müssen die Datenstruktur auswählen, die Ihren Anforderungen entspricht.
Rechtsfalte
2
Genau genommen ja, aber sie könnten sich als Vorteile herausstellen, wenn Sie diese Eigenschaften benötigen - das habe ich gemeint.
Patrick Oscity
@PatrickOscity: In einem solchen Fall wären sie sicherlich besser als Anforderungen zu kategorisieren ?
Leichtigkeitsrennen im Orbit
11
@greggreg Keyword-Listen haben noch einen weiteren impliziten Vorteil: Wir unterscheiden zwischen strukturierten und nicht strukturierten Daten. Karten sind äußerst nützlich für strukturierte Daten mit einem bekannten Satz von Schlüsseln und Schlüsselwörter nicht. Heutzutage werden die meisten Karten für strukturierte Daten verwendet, und wir belassen Schlüsselwörter für optionale. Wenn wir nur Karten hätten, würde ein großer Teil dieser Unterscheidung verloren gehen.
José Valim
1
Tatsächlich sind Karten der Weg von Erlang 18.
Papipo
12

Der Hauptvorteil von Keyword-Listen ist die Abwärtskompatibilität mit vorhandenen Elixier- und Erlang-Codebasen.

Sie fügen auch Syntaxzucker hinzu, wenn sie als Funktionsargumente verwendet werden, die beispielsweise einer Ruby-Syntax ähneln:

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

Der Hauptnachteil der Verwendung von Schlüsselwortlisten besteht darin, dass kein teilweiser Mustervergleich für sie durchgeführt werden kann:

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]

Erweitern wir es auf Funktionsargumente. Stellen Sie sich vor, wir müssen eine Multiclause-Funktion ausführen, die auf einem Wert einer der folgenden Optionen basiert:

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

Dies wird niemals Folgendes ausführen do_special_thing:

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

Mit Map-Argumenten funktioniert es:

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

Karten erlauben nur einen Eintrag für einen bestimmten Schlüssel, während Schlüsselwortlisten die Wiederholung des Schlüssels ermöglichen. Karten sind effizient (insbesondere wenn sie wachsen) und können für den Mustervergleich von Elixir verwendet werden.

Verwenden Sie im Allgemeinen Schlüsselwortlisten für Dinge wie Befehlszeilenparameter und zum Weitergeben von Optionen und verwenden Sie Maps (oder eine andere Datenstruktur, das HashDict), wenn Sie ein assoziatives Array wünschen.

Subhash Chandra
quelle