Lợi ích của Danh sách Từ khoá là gì?


101

Trong elixir, chúng tôi có Bản đồ:

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

Chúng tôi cũng có Danh sách Từ khoá:

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

Tại sao cả hai?

Cú pháp? Có phải vì Danh sách từ khóa có cú pháp linh hoạt hơn cho phép chúng được xác định mà không có dấu ngoặc nhọn và thậm chí không có dấu ngoặc như tham số cuối cùng của một lệnh gọi hàm không? Vậy tại sao không cung cấp cho Maps đường cú pháp này?

Chìa khóa trùng lặp? Có phải vì Danh sách Từ khoá có thể có các khoá trùng lặp không? Tại sao bạn muốn cả quyền truy cập Kiểu bản đồ và các khóa trùng lặp?

Hiệu suất? Có phải vì Danh sách Từ khoá có hiệu suất tốt hơn không? Vậy tại sao lại có Bản đồ? Và không phải bản đồ sẽ hiệu quả hơn trong việc tra cứu các thành viên theo từng khóa hơn là một danh sách các bộ giá trị?

JS Array và Ruby Hash như ngoại hình? Là nó?

Tôi hiểu rằng về mặt cấu trúc, chúng là các biểu diễn dữ liệu khác nhau. Đối với tôi, có vẻ như Danh sách từ khóa trong thuốc tiên có tác dụng làm phức tạp ngôn ngữ thông qua cú pháp đặc biệt (3 biến thể cú pháp khác nhau), trùng lặp trường hợp sử dụng với bản đồ và lợi ích không rõ ràng.

Lợi ích của việc sử dụng Danh sách Từ khoá là gì?

Câu trả lời:


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

Danh sách từ khóa nhẹ và có cấu trúc đơn giản bên dưới, điều này làm cho chúng rất linh hoạt. Bạn có thể coi chúng như một đường cú pháp trên đầu một quy ước Erlang, giúp bạn dễ dàng giao tiếp với Erlang mà không cần viết mã quá xấu. Ví dụ: danh sách từ khóa được sử dụng để đại diện cho các đối số của hàm, là một thuộc tính được kế thừa từ Erlang. Trong một số trường hợp, danh sách từ khóa là lựa chọn duy nhất của bạn, đặc biệt nếu bạn cần khóa trùng lặp hoặc đặt hàng. Chúng chỉ đơn giản là có các thuộc tính khác với các lựa chọn thay thế khác, khiến chúng phù hợp hơn với một số tình huống và ít phù hợp hơn với những tình huống khác.

Bản đồ (và Cấu trúc) được sử dụng để lưu trữ dữ liệu trọng tải thực tế, vì chúng có triển khai dựa trên băm. Danh sách từ khóa bên trong chỉ là danh sách cần được duyệt qua cho mỗi thao tác, vì vậy chúng không có các thuộc tính của cấu trúc dữ liệu khóa-giá trị cổ điển như truy cập thời gian liên tục.

Elixir cũng được giới thiệu HashDictnhư một giải pháp thay thế cho hiệu suất kém của bản đồ tại thời điểm nó được viết . Tuy nhiên, điều này hiện đã được khắc phục kể từ Elixir 1.0.5 / Erlang 18.0 và HashDict sẽ không được dùng trong các phiên bản trong tương lai .

Nếu bạn tìm hiểu sâu hơn về thư viện chuẩn Erlang, thậm chí còn có nhiều cấu trúc dữ liệu lưu trữ các cặp khóa / giá trị:

  • proplists - tương tự như danh sách từ khóa Elixir
  • bản đồ - giống như bản đồ Elixir
  • dict - từ điển khóa-giá trị được xây dựng từ nguyên thủy Erlang
  • gb_trees - cây cân bằng chung

Bạn cũng có các tùy chọn này khi cần lưu trữ các cặp khóa / giá trị trên nhiều quy trình và / hoặc máy ảo:

  • ets / dets - (dựa trên đĩa) Lưu trữ có thời hạn Erlang
  • mnesia - cơ sở dữ liệu phân tán

¹ Nói chung, nhưng tất nhiên nó phụ thuộc ™.

² Trường hợp tốt nhất là chỉ đưa trước vào danh sách.

³ Áp dụng cho Elixir 1.0.5 trở lên, có thể chậm hơn trong các phiên bản cũ hơn.

HashDicthiện không được dùng nữa.

⁵ Yêu cầu tìm kiếm tuyến tính mà trung bình quét một nửa các phần tử.


1
Việc cho phép các khóa trùng lặp và được đặt hàng không phải là lợi ích mà là các thuộc tính khác nhau. Bạn cần chọn cấu trúc dữ liệu phù hợp với nhu cầu của mình.
sang phải

2
Nói đúng ra là có, nhưng chúng có thể mang lại lợi ích nếu bạn cần những tài sản đó - đó là ý tôi.
Patrick Oscity

@PatrickOscity: Trong trường hợp như vậy, chắc chắn chúng sẽ được phân loại thành yêu cầu tốt hơn ?
Các cuộc đua ánh sáng trong quỹ đạo vào

11
@greggreg Có một lợi ích ngầm khác của việc có danh sách từ khóa: chúng tôi phân biệt dữ liệu có cấu trúc và không có cấu trúc. Bản đồ cực kỳ hữu ích cho dữ liệu có cấu trúc với một tập hợp các khóa đã biết còn từ khóa thì không. Ngày nay, hầu hết việc sử dụng bản đồ là cho dữ liệu có cấu trúc và chúng tôi để lại từ khóa cho những từ khóa tùy chọn. Nếu chúng ta chỉ có bản đồ, tôi nghĩ rằng một phần tốt của sự khác biệt này sẽ bị mất.
José Valim

1
Trong thực tế nó đã làm, bản đồ là cách để đi từ erlang 18.
Papipo

12

Lợi ích chính của danh sách từ khóa là khả năng tương thích ngược với cơ sở mã elixir và erlang hiện có.

Chúng cũng thêm đường cú pháp nếu được sử dụng làm đối số của hàm tương tự như cú pháp ruby:

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

Hạn chế chính của việc sử dụng danh sách từ khóa là không thể thực hiện đối sánh một phần mẫu trên chúng:

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]

Hãy mở rộng nó thành các đối số hàm. Hãy tưởng tượng chúng ta cần xử lý một hàm đa tạm dừng dựa trên giá trị của một trong các tùy chọn:

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

Điều này sẽ không bao giờ thực hiện do_special_thing:

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

Với các đối số bản đồ, nó sẽ hoạt động:

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

2

Bản đồ chỉ cho phép một mục nhập cho một khóa cụ thể, trong khi danh sách từ khóa cho phép lặp lại khóa. Bản đồ hiệu quả (đặc biệt là khi chúng phát triển) và chúng có thể được sử dụng để đối sánh mẫu của Elixir.

Nói chung, hãy sử dụng danh sách từ khóa cho những thứ như tham số dòng lệnh và để chuyển xung quanh các tùy chọn và sử dụng bản đồ (hoặc một cấu trúc dữ liệu khác, HashDict) khi bạn muốn một mảng kết hợp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.