¿Cuál es el beneficio de las listas de palabras clave?

101

En elixir tenemos Mapas:

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

También tenemos listas de palabras clave:

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

¿Por qué ambos?

¿Sintaxis? ¿Es porque las listas de palabras clave tienen una sintaxis más flexible que les permite ser definidas sin curlys e incluso sin corchetes como último parámetro de una llamada de función? Entonces, ¿por qué no darle a Maps este azúcar sintáctico?

¿Claves duplicadas? ¿Es porque las listas de palabras clave pueden tener claves duplicadas? ¿Por qué querría tanto acceso al estilo de mapa como claves duplicadas?

¿Actuación? ¿Es porque las listas de palabras clave tienen un mejor rendimiento? Entonces, ¿por qué tener Maps? ¿Y no deberían los mapas ser más eficaces para buscar miembros por clave que una lista de tuplas?

¿JS Array y Ruby Hash como apariencia? ¿Es asi?

Entiendo que estructuralmente son diferentes representaciones de datos. A mí me parece que las listas de palabras clave en elixir sirven para complicar el lenguaje a través de una sintaxis excepcional (3 variantes sintácticas diferentes), superposición de casos de uso con mapas y un beneficio poco claro.

¿Cuál es el beneficio de utilizar listas de palabras clave?

Greggreg
fuente

Respuestas:

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

Las listas de palabras clave son livianas y tienen una estructura simple debajo, lo que las hace muy flexibles. Puede pensar en ellos como azúcar de sintaxis además de una convención de Erlang, lo que facilita la interfaz con Erlang sin escribir un código demasiado feo. Por ejemplo, las listas de palabras clave se utilizan para representar argumentos de funciones, que es una propiedad heredada de Erlang. En algunos casos, las listas de palabras clave son su única opción, especialmente si necesita claves o pedidos duplicados. Simplemente tienen propiedades diferentes a las otras alternativas, lo que las hace más adecuadas para unas situaciones y menos para otras.

Los mapas (y estructuras) se utilizan para almacenar datos reales de carga útil, ya que tienen una implementación basada en hash. Las listas de palabras clave internamente son solo listas que deben recorrerse para cada operación, por lo que no tienen las propiedades de las estructuras clásicas de datos clave-valor, como el acceso en tiempo constante.

Elixir también se introdujo HashDictcomo una solución para el bajo rendimiento de los mapas en el momento en que se escribió . Sin embargo, esto está solucionado ahora a partir de Elixir 1.0.5 / Erlang 18.0 y HashDict quedará obsoleto en versiones futuras .

Si profundiza en la biblioteca estándar de Erlang, hay incluso más estructuras de datos que almacenan pares clave / valor:

  • proplists - similar a las listas de palabras clave de Elixir
  • mapas - igual que los mapas de Elixir
  • dict : diccionarios de valores-clave creados a partir de primitivas de Erlang
  • gb_trees - árbol balanceado general

También tiene estas opciones cuando necesita almacenar pares clave / valor en múltiples procesos y / o VM:

  • ets / dets - (basado en disco) almacenamiento de términos de Erlang
  • mnesia - base de datos distribuida

¹ En general, pero por supuesto que depende ™.

² En el mejor de los casos, simplemente se antepone a una lista.

³ Se aplica a Elixir 1.0.5 y superior, puede ser más lento en versiones anteriores.

HashDict ahora está en desuso.

⁵ Requiere una búsqueda lineal que, en promedio, escanea la mitad de los elementos.

Patrick Oscity
fuente
1
Permitir claves duplicadas y ordenar no son beneficios, sino propiedades diferentes. Debe elegir la estructura de datos que se adapte a sus necesidades.
margen derecho
2
Estrictamente hablando, sí, pero podrían resultar beneficiosos si necesita esas propiedades, eso es lo que quise decir.
Patrick Oscity
@PatrickOscity: En tal caso, ¿seguramente estarían mejor categorizados como requisitos ?
Lightness Races in Orbit
11
@greggreg Existe otro beneficio implícito de tener listas de palabras clave: hacemos una distinción entre datos estructurados y no estructurados. Los mapas son extremadamente útiles para datos estructurados con un conjunto conocido de claves y las palabras clave no lo son. Hoy en día, la mayor parte del uso de mapas es para datos estructurados y dejamos las palabras clave para las opcionales. Si tan solo tuviéramos mapas, creo que se perdería buena parte de esta distinción.
José Valim
1
De hecho lo hizo, los mapas son el camino a seguir desde erlang 18.
Papipo
12

El principal beneficio de las listas de palabras clave es la compatibilidad con versiones anteriores de la base de código elixir y erlang existente.

También agregan azúcar de sintaxis si se usan como argumentos de funciones que se asemejan, por ejemplo, a una sintaxis ruby:

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

El principal inconveniente de utilizar listas de palabras clave es que no es posible realizar una coincidencia parcial de patrones en ellas:

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]

Extengámoslo a los argumentos de la función. Imagine que necesitamos manejar una función de múltiples cláusulas en función del valor de una de las opciones:

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

Esto nunca ejecutará do_special_thing:

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

Con argumentos de mapa funcionará:

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

Los mapas permiten solo una entrada para una clave en particular, mientras que las listas de palabras clave permiten que se repita la clave. Los mapas son eficientes (especialmente a medida que crecen) y se pueden usar en la coincidencia de patrones de Elixir.

En general, use listas de palabras clave para cosas como parámetros de línea de comandos y para pasar opciones, y use mapas (u otra estructura de datos, el HashDict) cuando desee una matriz asociativa.

Subhash Chandra
fuente