Elixir关键字列表 #
一、关键字列表基础 #
1.1 定义 #
关键字列表是两元素元组的列表,第一个元素必须是原子:
elixir
iex(1)> [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex(2)> [{:a, 1}, {:b, 2}, {:c, 3}]
[a: 1, b: 2, c: 3]
1.2 特性 #
- 本质是列表
- 键必须是原子
- 键可以重复
- 保持插入顺序
elixir
iex(1)> [a: 1, a: 2, a: 3]
[a: 1, a: 2, a: 3]
iex(2)> is_list([a: 1, b: 2])
true
1.3 空关键字列表 #
elixir
iex(1)> []
[]
二、访问与操作 #
2.1 访问值 #
elixir
iex(1)> kw = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex(2)> kw[:a]
1
iex(3)> kw[:unknown]
nil
iex(4)> Keyword.get(kw, :a)
1
iex(5)> Keyword.get(kw, :unknown, "default")
"default"
2.2 重复键 #
elixir
iex(1)> kw = [a: 1, a: 2, a: 3]
[a: 1, a: 2, a: 3]
iex(2)> kw[:a]
1
iex(3)> Keyword.get_values(kw, :a)
[1, 2, 3]
2.3 添加键值 #
elixir
iex(1)> kw = [a: 1, b: 2]
[a: 1, b: 2]
iex(2)> Keyword.put(kw, :c, 3)
[a: 1, b: 2, c: 3]
iex(3)> Keyword.put_new(kw, :a, 10)
[a: 1, b: 2]
iex(4)> kw ++ [c: 3]
[a: 1, b: 2, c: 3]
2.4 删除键 #
elixir
iex(1)> kw = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex(2)> Keyword.delete(kw, :b)
[a: 1, c: 3]
iex(3)> Keyword.delete_first(kw, :a)
[a: 2, a: 3]
2.5 更新值 #
elixir
iex(1)> kw = [a: 1, b: 2]
[a: 1, b: 2]
iex(2)> Keyword.put(kw, :a, 10)
[a: 10, b: 2]
三、Keyword模块函数 #
3.1 查询 #
elixir
iex(1)> kw = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex(2)> Keyword.has_key?(kw, :a)
true
iex(3)> Keyword.has_key?(kw, :d)
false
iex(4)> Keyword.keys(kw)
[:a, :b, :c]
iex(5)> Keyword.values(kw)
[1, 2, 3]
iex(6)> Keyword.size(kw)
3
3.2 取值 #
elixir
iex(1)> kw = [a: 1, b: 2]
[a: 1, b: 2]
iex(2)> Keyword.fetch(kw, :a)
{:ok, 1}
iex(3)> Keyword.fetch(kw, :unknown)
:error
iex(4)> Keyword.fetch!(kw, :a)
1
iex(5)> Keyword.pop(kw, :a)
{1, [b: 2]}
iex(6)> Keyword.pop_first(kw, :a)
{1, [a: 2, a: 3]}
3.3 合并 #
elixir
iex(1)> Keyword.merge([a: 1], [b: 2])
[a: 1, b: 2]
iex(2)> Keyword.merge([a: 1, b: 1], [b: 2])
[a: 1, b: 2]
3.4 转换 #
elixir
iex(1)> Keyword.to_list([a: 1, b: 2])
[a: 1, b: 2]
iex(2)> Enum.to_list([a: 1, b: 2])
[a: 1, b: 2]
iex(3)> Enum.into([a: 1, b: 2], %{})
%{a: 1, b: 2}
四、函数选项 #
4.1 基本用法 #
关键字列表常用于函数选项:
elixir
defmodule HTTP do
def get(url, opts \\ []) do
timeout = Keyword.get(opts, :timeout, 5000)
headers = Keyword.get(opts, :headers, [])
follow_redirects = Keyword.get(opts, :follow_redirects, true)
IO.puts("GET #{url}")
IO.puts("Timeout: #{timeout}")
IO.puts("Headers: #{inspect(headers)}")
IO.puts("Follow redirects: #{follow_redirects}")
end
end
HTTP.get("https://example.com", timeout: 10_000, headers: [{"Authorization", "Bearer token"}])
4.2 默认值 #
elixir
defmodule Config do
def build(opts \\ []) do
%{
host: Keyword.get(opts, :host, "localhost"),
port: Keyword.get(opts, :port, 8080),
ssl: Keyword.get(opts, :ssl, false),
timeout: Keyword.get(opts, :timeout, 5000)
}
end
end
Config.build(port: 3000, ssl: true)
4.3 选项验证 #
elixir
defmodule Validator do
def validate_opts(opts, allowed_keys) do
Enum.each(opts, fn {key, _value} ->
unless key in allowed_keys do
raise ArgumentError, "unknown option: #{key}"
end
end)
opts
end
end
五、模式匹配 #
5.1 列表匹配 #
elixir
iex(1)> [a: a, b: b] = [a: 1, b: 2]
[a: 1, b: 2]
iex(2)> a
1
iex(3)> b
2
5.2 头尾匹配 #
elixir
iex(1)> [head | tail] = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex(2)> head
{:a, 1}
iex(3)> tail
[b: 2, c: 3]
5.3 限制 #
关键字列表的模式匹配有限制,不能像映射那样部分匹配:
elixir
iex(1)> [a: a] = [a: 1, b: 2]
** (MatchError) no match of right hand side value: [a: 1, b: 2]
六、与映射的比较 #
6.1 语法比较 #
elixir
[a: 1, b: 2]
%{a: 1, b: 2}
6.2 特性比较 #
| 特性 | 关键字列表 | 映射 |
|---|---|---|
| 本质 | 列表 | 映射 |
| 键类型 | 仅原子 | 任意 |
| 键重复 | 允许 | 不允许 |
| 顺序 | 有序 | 无序 |
| 查找性能 | O(n) | O(log n) |
| 模式匹配 | 有限 | 完整 |
6.3 选择建议 #
使用关键字列表:
- 函数选项
- 需要保持顺序
- 允许重复键
使用映射:
- 需要快速查找
- 键不是原子
- 需要完整模式匹配
七、Ecto查询示例 #
7.1 查询选项 #
elixir
import Ecto.Query
from u in User,
where: u.age > 18,
select: u.name,
order_by: [desc: u.inserted_at],
limit: 10
7.2 动态查询 #
elixir
def build_query(base_query, opts) do
query = base_query
query = if opts[:order_by] do
order_by(query, ^opts[:order_by])
else
query
end
query = if opts[:limit] do
limit(query, ^opts[:limit])
else
query
end
query
end
八、Phoenix示例 #
8.1 路由选项 #
elixir
scope "/api", Api do
pipe_through :api
resources "/users", UserController, only: [:index, :show, :create]
resources "/posts", PostController, except: [:delete]
end
8.2 视图选项 #
elixir
def render("user.json", %{user: user}) do
%{
id: user.id,
name: user.name,
email: user.email
}
end
九、最佳实践 #
9.1 函数选项模式 #
elixir
defmodule MyModule do
@default_opts [
timeout: 5000,
retries: 3,
debug: false
]
def do_something(arg, opts \\ []) do
opts = Keyword.merge(@default_opts, opts)
timeout = Keyword.fetch!(opts, :timeout)
retries = Keyword.fetch!(opts, :retries)
debug = Keyword.fetch!(opts, :debug)
IO.puts("Timeout: #{timeout}")
IO.puts("Retries: #{retries}")
IO.puts("Debug: #{debug}")
end
end
9.2 选项文档 #
elixir
@doc """
Do something useful.
## Options
* `:timeout` - timeout in milliseconds (default: 5000)
* `:retries` - number of retries (default: 3)
* `:debug` - enable debug mode (default: false)
## Examples
MyModule.do_something("arg", timeout: 10_000, debug: true)
"""
def do_something(arg, opts \\ [])
十、总结 #
本章学习了:
| 操作 | 示例 |
|---|---|
| 创建 | [a: 1, b: 2] |
| 访问 | kw[:key] |
| 添加 | Keyword.put(kw, :key, value) |
| 删除 | Keyword.delete(kw, :key) |
| 获取所有值 | Keyword.get_values(kw, :key) |
| 合并 | Keyword.merge(kw1, kw2) |
准备好学习MapSet与Range了吗?让我们进入下一章。
最后更新:2026-03-27