Apa manfaat Daftar Kata Kunci?

101

Di elixir kami memiliki Maps:

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

Kami juga memiliki Daftar Kata Kunci:

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

Mengapa keduanya?

Sintaksis? Apakah karena Daftar Kata Kunci memiliki sintaks yang lebih fleksibel yang memungkinkannya untuk ditentukan tanpa tanda kurung dan bahkan tanpa tanda kurung sebagai parameter terakhir dari panggilan fungsi? Lalu mengapa tidak memberi Maps gula sintaksis ini?

Kunci Duplikat? Apakah karena Daftar Kata Kunci dapat memiliki kunci duplikat? Mengapa Anda menginginkan akses gaya Peta dan kunci duplikat?

Performa? Apakah karena Daftar Kata Kunci memiliki kinerja yang lebih baik? Lalu kenapa punya Maps? Dan bukankah peta lebih baik dalam mencari anggota dengan kunci daripada daftar tupel?

Penampilan seperti JS Array dan Ruby Hash? Itu saja?

Saya memahami bahwa secara struktural mereka adalah representasi data yang berbeda. Bagi saya, tampaknya Daftar Kata Kunci di elixir berfungsi untuk memperumit bahasa melalui sintaksis yang luar biasa (3 varian sintaksis yang berbeda), kasus penggunaan yang tumpang tindih dengan peta, dan manfaat yang tidak jelas.

Apa keuntungan menggunakan Daftar Kata Kunci?

greggreg.dll
sumber

Jawaban:

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

Daftar kata kunci ringan dan memiliki struktur sederhana di bawahnya, yang membuatnya sangat fleksibel. Anda dapat menganggapnya sebagai gula sintaks di atas konvensi Erlang, membuatnya mudah untuk berinteraksi dengan Erlang tanpa menulis kode yang terlalu jelek. Misalnya, daftar kata kunci digunakan untuk mewakili argumen fungsi, yang merupakan properti yang diwarisi dari Erlang. Dalam beberapa kasus, daftar kata kunci adalah satu-satunya pilihan Anda, terutama jika Anda memerlukan kunci atau urutan duplikat. Mereka hanya memiliki sifat yang berbeda dari alternatif lain, yang membuatnya lebih cocok untuk beberapa situasi dan lebih sedikit untuk yang lain.

Maps (dan Structs) digunakan untuk menyimpan data payload aktual, karena keduanya memiliki implementasi berbasis hash. Daftar kata kunci secara internal hanyalah daftar yang perlu dilalui untuk setiap operasi, sehingga daftar tersebut tidak memiliki properti struktur data nilai kunci klasik seperti akses waktu yang konstan.

Elixir juga diperkenalkan HashDictsebagai solusi untuk kinerja peta yang buruk pada saat ditulis . Namun, ini telah diperbaiki sekarang pada Elixir 1.0.5 / Erlang 18.0 dan tidak HashDict akan digunakan lagi di versi mendatang .

Jika Anda menggali lebih dalam pustaka standar Erlang, ada lebih banyak lagi struktur data yang menyimpan pasangan kunci / nilai:

  • proplists - mirip dengan daftar kata kunci Elixir
  • peta - sama seperti peta Elixir
  • dict - kamus nilai kunci yang dibuat dari primitif Erlang
  • gb_trees - pohon umum seimbang

Anda juga memiliki opsi ini saat Anda perlu menyimpan pasangan kunci / nilai di beberapa proses dan / atau VM:

  • ets / dets - (berbasis disk) penyimpanan istilah Erlang
  • mnesia - database terdistribusi

¹ Secara umum, tapi tentu saja itu tergantung ™.

² Kasus terbaik hanyalah mempersiapkan daftar.

³ Berlaku untuk Elixir 1.0.5 dan yang lebih baru, mungkin lebih lambat di versi yang lebih lama.

HashDictsekarang tidak digunakan lagi.

⁵ Membutuhkan pencarian linier yang rata-rata memindai setengah dari elemen.

Patrick Oscity
sumber
1
Mengizinkan kunci duplikat dan dipesan bukanlah manfaat, tetapi properti yang berbeda. Anda perlu memilih struktur data yang sesuai dengan kebutuhan Anda.
kanan
2
Sebenarnya, ya, tetapi itu mungkin menjadi manfaat jika Anda membutuhkan properti itu - itulah yang saya maksud.
Patrick Oscity
@PatrickOscity: Dalam kasus seperti itu, tentunya mereka akan lebih baik dikategorikan sebagai persyaratan ?
Balapan Ringan di Orbit
11
@greggreg Ada manfaat implisit lain dari memiliki daftar kata kunci: kami membuat perbedaan tentang data terstruktur dan non-terstruktur. Peta sangat berguna untuk data terstruktur dengan sekumpulan kunci yang diketahui dan kata kunci tidak. Saat ini, sebagian besar penggunaan peta adalah untuk data terstruktur dan kami meninggalkan kata kunci untuk yang opsional. Jika kita hanya memiliki peta, saya pikir sebagian besar perbedaan ini akan hilang.
José Valim
1
Faktanya, peta adalah cara untuk pergi dari erlang 18.
Papipo
12

Manfaat utama daftar kata kunci adalah kompatibilitas mundur dengan basis kode elixir dan erlang yang ada.

Mereka juga menambahkan gula sintaks jika digunakan sebagai argumen fungsi yang menyerupai misalnya sintaks ruby:

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

Kelemahan utama penggunaan daftar kata kunci adalah tidak mungkin melakukan pencocokan pola parsial pada daftar tersebut:

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]

Mari kita kembangkan ke argumen fungsi. Bayangkan kita perlu menangani fungsi multiklause berdasarkan nilai dari salah satu opsi:

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

Ini tidak akan pernah mengeksekusi do_special_thing:

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

Dengan argumen peta, ini akan berfungsi:

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

Peta hanya mengizinkan satu entri untuk kunci tertentu, sedangkan daftar kata kunci memungkinkan kunci tersebut diulang. Peta efisien (terutama saat mereka tumbuh), dan mereka dapat digunakan dalam pencocokan pola Elixir.

Secara umum, gunakan daftar kata kunci untuk hal-hal seperti parameter baris perintah dan untuk menyebarkan opsi, dan gunakan peta (atau struktur data lain, HashDict) saat Anda menginginkan array asosiatif.

Subhash Chandra
sumber