В чем преимущество списков ключевых слов?

101

В эликсире есть Карты:

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

У нас также есть списки ключевых слов:

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

Почему оба?

Синтаксис? Это потому, что списки ключевых слов имеют более гибкий синтаксис, позволяющий определять их без фигурных скобок и даже без скобок в качестве последнего параметра вызова функции? Тогда почему бы не дать Maps этот синтаксический сахар?

Повторяющиеся ключи? Это потому, что списки ключевых слов могут иметь повторяющиеся ключи? Зачем вам нужен и доступ к стилю карты, и дублирующиеся ключи?

Производительность? Это потому, что списки ключевых слов более эффективны? Тогда зачем нужны Карты? И не должны ли карты быть более производительными при поиске членов по ключу, чем список кортежей?

JS Array и Ruby Hash похожи на внешний вид? Это оно?

Я понимаю, что структурно это разные представления данных. Мне кажется, что списки ключевых слов в elixir служат для усложнения языка за счет исключительного синтаксиса (3 различных синтаксических варианта), совпадения вариантов использования с картами и неясного преимущества.

В чем преимущество использования списков ключевых слов?

Греггрег
источник

Ответы:

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

Списки ключевых слов легкие и имеют простую структуру, что делает их очень гибкими. Вы можете думать о них как о синтаксическом сахаре на вершине соглашения Erlang, что упрощает взаимодействие с Erlang без написания слишком уродливого кода. Например, списки ключевых слов используются для представления аргументов функции, что является свойством, унаследованным от Erlang. В некоторых случаях списки ключевых слов - ваш единственный выбор, особенно если вам нужны повторяющиеся ключи или порядок. Просто они обладают другими свойствами, чем другие альтернативы, что делает их более подходящими для одних ситуаций и менее подходящими для других.

Карты (и структуры) используются для хранения фактических данных полезной нагрузки, поскольку они имеют реализацию на основе хешей. Списки ключевых слов внутри - это просто списки, которые необходимо просмотреть для каждой операции, поэтому они не обладают свойствами классических структур данных ключ-значение, таких как доступ с постоянным временем.

Elixir также был введен HashDictкак временное решение проблемы низкой производительности карт на момент написания . Однако сейчас это исправлено в Elixir 1.0.5 / Erlang 18.0 и HashDict будет считаться устаревшим в будущих версиях .

Если углубиться в стандартную библиотеку Erlang, можно увидеть еще больше структур данных, которые хранят пары ключ / значение:

  • proplists - аналогично спискам ключевых слов в Elixir
  • карты - такие же, как карты Эликсира
  • dict - словари ключ-значение, построенные из примитивов Erlang
  • gb_trees - общее сбалансированное дерево

У вас также есть эти варианты, когда вам нужно хранить пары ключ / значение в нескольких процессах и / или виртуальных машинах:

  • ЭТС / дец - ( на основе диска) Эрланга длительного хранения
  • mnesia - распределенная база данных

¹ Вообще говоря, но это конечно зависит ™.

² В лучшем случае это просто добавление к списку.

³ Относится к Elixir 1.0.5 и выше, может работать медленнее в старых версиях.

HashDictтеперь устарела.

⁵ Требуется линейный поиск, который в среднем сканирует половину элементов.

Патрик Оссити
источник
1
Разрешение дублирования ключей и заказ - это не преимущества, а разные свойства. Вам нужно выбрать структуру данных, которая соответствует вашим потребностям.
правый фолд
2
Строго говоря, да, но они могут оказаться полезными, если вам понадобятся эти свойства - вот что я имел в виду.
Patrick Oscity
@PatrickOscity: В таком случае, конечно, их лучше отнести к категории требований ?
Гонки легкости на орбите,
11
@greggreg Есть еще одно неявное преимущество наличия списков ключевых слов: мы делаем различие между структурированными и неструктурированными данными. Карты чрезвычайно полезны для структурированных данных с известным набором ключей, а ключевые слова - нет. Сегодня карты чаще всего используются для структурированных данных, и мы оставляем ключевые слова для дополнительных. Если бы у нас были только карты, я думаю, большая часть этого различия была бы потеряна.
Хосе Валим
1
На самом деле это так, карты - это способ уйти с эрланга 18.
Папипо
12

Основное преимущество списков ключевых слов - обратная совместимость с существующей кодовой базой elixir и erlang.

Они также добавляют синтаксический сахар, если используются в качестве аргументов функций, что похоже, например, на синтаксис ruby:

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

Основным недостатком использования списков ключевых слов является то, что для них невозможно выполнить частичное сопоставление с образцом:

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]

Давайте расширим его до аргументов функции. Представьте, что нам нужно обработать функцию с несколькими исключениями на основе значения одного из параметров:

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

Это никогда не выполнит do_special_thing:

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

С аргументами карты это будет работать:

fun2("arg", %{opt1: nil, opt2: "some value"})
doing special thing
Волды
источник
2

Карты допускают только одну запись для определенного ключа, тогда как списки ключевых слов допускают повторение ключа. Карты эффективны (особенно по мере их роста), и их можно использовать для сопоставления шаблонов в Elixir.

В общем, используйте списки ключевых слов для таких вещей, как параметры командной строки и для передачи параметров, и используйте карты (или другую структуру данных, HashDict), когда вам нужен ассоциативный массив.

Субхаш Чандра
источник