Jakie są zalety list słów kluczowych?

101

W eliksirze mamy mapy:

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

Mamy również listy słów kluczowych:

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

Dlaczego jedno i drugie?

Składnia? Czy dzieje się tak dlatego, że listy słów kluczowych mają bardziej elastyczną składnię, dzięki czemu mogą być definiowane bez zawiasów, a nawet bez nawiasów jako ostatni parametr wywołania funkcji? Dlaczego więc nie dać Mapom tego cukru syntaktycznego?

Zduplikowane klucze? Czy to dlatego, że listy słów kluczowych mogą mieć zduplikowane klucze? Dlaczego chcesz mieć zarówno dostęp w stylu mapy, jak i zduplikowane klucze?

Występ? Czy to dlatego, że listy słów kluczowych mają lepszą wydajność? Dlaczego więc masz Mapy? I czy mapy nie powinny skuteczniej wyszukiwać członków według klucza niż lista krotek?

JS Array i Ruby Hash jak wygląd? Czy to to?

Rozumiem, że strukturalnie są to różne reprezentacje danych. Wydaje mi się, że listy słów kluczowych w eliksirze komplikują język poprzez wyjątkową składnię (3 różne warianty składni), przypadki użycia pokrywają się z mapami i niejasne korzyści.

Jakie są zalety korzystania z list słów kluczowych?

greggreg
źródło

Odpowiedzi:

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

Listy słów kluczowych są lekkie i mają prostą strukturę, co czyni je bardzo elastycznymi. Możesz myśleć o nich jako o cukrze składniowym na szczycie konwencji Erlanga, ułatwiając interfejs z Erlang bez pisania zbyt brzydkiego kodu. Na przykład listy słów kluczowych służą do reprezentowania argumentów funkcji, które są właściwością dziedziczoną po Erlang. W niektórych przypadkach listy słów kluczowych są jedynym wyborem, zwłaszcza jeśli potrzebujesz zduplikowanych kluczy lub kolejności. Po prostu mają inne właściwości niż inne alternatywy, co sprawia, że ​​są bardziej odpowiednie w niektórych sytuacjach, a mniej w innych.

Mapy (i struktury) są używane do przechowywania rzeczywistych danych ładunku, ponieważ mają implementację opartą na skrótach. Wewnętrzne listy słów kluczowych są po prostu listami, które należy przejść dla każdej operacji, więc nie mają właściwości klasycznych struktur danych klucz-wartość, takich jak stały dostęp w czasie.

Elixir został również wprowadzony HashDictjako obejście słabej wydajności map w czasie, gdy został napisany . Jednak zostało to naprawione od wersji Elixir 1.0.5 / Erlang 18.0 i HashDict zostanie wycofane w przyszłych wersjach .

Jeśli zagłębisz się w standardową bibliotekę Erlang, istnieje jeszcze więcej struktur danych, które przechowują pary klucz / wartość:

  • proplists - podobne do list słów kluczowych Elixir
  • mapy - takie same jak mapy Elixir
  • dict - słowniki klucz-wartość zbudowane z prymitywów Erlanga
  • gb_trees - ogólne zrównoważone drzewo

Te opcje są również dostępne, gdy chcesz przechowywać pary klucz / wartość w wielu procesach i / lub maszynach wirtualnych:

  • ets / dets - (dyskowe) przechowywanie terminów Erlang
  • mnesia - rozproszona baza danych

¹ Ogólnie rzecz biorąc, ale oczywiście to zależy ™.

² Najlepszym przypadkiem jest po prostu poprzedzanie listy.

³ Dotyczy Elixir 1.0.5 i nowszych, może działać wolniej w starszych wersjach.

HashDictjest obecnie przestarzały.

⁵ Wymaga wyszukiwania liniowego, które skanuje średnio połowę elementów.

Patrick Oscity
źródło
1
Zezwolenie na zduplikowane klucze i zamówienie to nie korzyści, ale różne właściwości. Musisz wybrać strukturę danych, która odpowiada Twoim potrzebom.
prawy bok
2
Ściśle mówiąc, tak, ale mogą się okazać korzyściami, jeśli potrzebujesz tych właściwości - o to mi chodziło.
Patrick Oscity
@PatrickOscity: W takim przypadku na pewno lepiej by je sklasyfikowano jako wymagania ?
Wyścigi lekkości na orbicie
11
@greggreg Istnieje jeszcze jedna niejawna zaleta posiadania list słów kluczowych: rozróżniamy dane ustrukturyzowane i nieustrukturyzowane. Mapy są niezwykle przydatne w przypadku danych strukturalnych ze znanym zestawem kluczy, a słowa kluczowe nie. Obecnie większość zastosowań map dotyczy danych strukturalnych, a słowa kluczowe pozostawiamy opcjonalnym. Gdybyśmy mieli tylko mapy, myślę, że znaczna część tego rozróżnienia zostałaby utracona.
José Valim,
1
W rzeczywistości tak, mapy są drogą do przejścia z erlang 18.
Papipo.
12

Główną zaletą list słów kluczowych jest zgodność wsteczna z istniejącymi bazami kodów eliksirów i erlang.

Dodają również cukier składniowy, jeśli są używane jako argumenty funkcji, które przypominają np. Składnię ruby:

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

Główną wadą korzystania z list słów kluczowych jest to, że nie można na nich przeprowadzić częściowego dopasowania do wzorca:

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]

Rozszerzmy to na argumenty funkcji. Wyobraź sobie, że musimy obsłużyć funkcję wielokrotnego użytku opartą na wartości jednej z opcji:

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

To nigdy nie wykona do_special_thing:

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

Z argumentami mapy zadziała:

fun2("arg", %{opt1: nil, opt2: "some value"})
doing special thing
Voldy
źródło
2

Mapy pozwalają tylko na jeden wpis dla określonego klucza, podczas gdy listy słów kluczowych pozwalają na powtórzenie klucza. Mapy są wydajne (zwłaszcza gdy rosną) i można ich używać w dopasowywaniu wzorców Elixiru.

Ogólnie rzecz biorąc, używaj list słów kluczowych do takich rzeczy, jak parametry wiersza poleceń i do przekazywania opcji, a także używaj map (lub innej struktury danych, HashDict), gdy potrzebujesz tablicy asocjacyjnej.

Subhash Chandra
źródło