Elixir 基础知识参考字典

要点

Elixir 基础层的名词按用途分成五组:匹配与数据存取、字符串与编码、枚举与流、进程与并发、调试与工程。每个条目给签名、一句话语义、最小示例和坑,示例自包含,粘进 iex 就能跑。字典是查的不是读的——叙事式的语法讲解在《Elixir 语法要点》,本文只做条目式速查,文末速查表是全量索引。截至本文(2025 年 5 月)稳定线为 Elixir 1.18.x,版本敏感条目单独标注。

匹配与数据存取

组内共性:这组条目都围绕”形状对齐”——Elixir 里取数据的正确姿势不是防御性判空,而是声明你要的形状。叙事版见语法要点篇,这里只留查表用的签名与坑。

匹配运算符 = 与 pin ^

{:ok, value} = {:ok, 42}    # 形状对齐才成功,value 绑定为 42
value = 100                 # 重新绑定(rebinding),合法
^value = 101                # **MatchError:^ 断言"必须是已有值 100"**

左边是模式右边是数据;= 失败抛 MatchError,不返回 nil。

⚠️ 坑:x = x + 1 不是”自增”,是先读右边旧值再重新绑定;而 ^x = x + 1 恒等于断言 x == x + 1,永远失败。

Map.get / Map.fetch / Map.fetch!

m = %{"a" => 1}

Map.get(m, "b")        # => nil(可给第三参当默认值)
Map.fetch(m, "b")      # => :error(命中则 {:ok, value})
Map.fetch!(m, "b")     # **KeyError**

取值三连:get 用于”缺了有默认行为”,fetch 用于”缺了要走错误分支”,fetch! 用于”缺了就是 bug,直接炸”。

⚠️ 坑:Map.get(m, "a") 返回 nil 时,分不清”键存在但值是 nil”和”键不存在”;需要区分就用 fetch。

struct 与更新语法 %{} |

defmodule User, do: defstruct([:name, :age])   # 定义具名 map

u = %User{name: "alice", age: 30}
u2 = %{u | age: 31}     # 更新已有键,返回新 struct,u 不变
u2.age                  # => 31

更新语法 %{struct_or_map | key: new_value} 只改已存在的键。

⚠️ 坑:用它”新增”键直接抛 KeyError(BadMapError 场景之一)——新增键只能整体重建 %{u | age: 31, email: "x"} 也不行,email 不在原 struct 字段里同样报错;struct 的键集合在 defstruct 时就锁死了。

字符串与编码

组内共性:Elixir 字符串是 UTF-8 二进制(binary),”字符”是字素(grapheme)不是字节。字符列表 charlist 是另一种东西,只在和 Erlang 互操作时出现。

插值、拼接与长度

name = "elixir"
"greet: #{name}"          # 插值,nil 会渲染成空串
"pre" <> "fix"            # => "prefix",拼接
String.length("你好")     # => 2(字素数)
byte_size("你好")         # => 6(UTF-8 字节数)

插值用 #{},拼接用 <>,长度按场景二选一。

⚠️ 坑:<> 的右侧必须是字符串,"a" <> name 里 name 是 nil 或整数直接抛 ArgumentError;按字节截断中文(binary_part)会截出半个 UTF-8 序列,截字符串用 String.slice。

内置 JSON 模块(Elixir 1.18+)

JSON.encode!(%{name: "elixir", tags: ["fp", "beam"]})
1. => "{\"name\":\"elixir\",\"tags\":[\"fp\",\"beam\"]}"

JSON.decode!(~s({"a": 1}))     # => %{"a" => 1}
JSON.decode(~s(not json))      # => {:error, %JSON.DecodeError{}}

1.18 起自带 JSON 编解码,底层包装 Erlang/OTP 27 的内置 JSON,基本 API 对齐 Jason 库;自定义类型的编码走 JSON.Encoder 协议,可在 defstruct 上 @derive {JSON.Encoder, only: [:id, :name]}。

⚠️ 坑:带 ! 的版本解析失败直接抛异常,适合”坏了就该炸”的场景;要拿 error tuple 用不带叹号的 JSON.decode/2。1.17 及更早没有内置 JSON,老项目仍在用第三方 Jason,两套 API 相近但模块名不同。

charlist 与 ~c

'abc'            # 字符列表:整数列表 [97, 98, 99]
~c"abc"          # 推荐写法,等价于上面
'abc' == "abc"   # => false

charlist 是与 Erlang 老模块(如 :crypto 的部分接口)互操作时的形态,新业务代码一律用字符串。

⚠️ 坑:单引号在 Elixir 不是字符串是 charlist,复制 Erlang 例程到 Elixir 时最容易踩;1.18 的 mix format --migrate 会把旧 sigil 写法规范化成 ~c。

枚举与流

组内共性:List 是单向链表,Enum 的很多操作对大列表是 O(n);Stream 是惰性的 Enum,数据量大或要拼接多步变换时才用。

Enum 高频函数

orders = [%{id: 1, amt: 30}, %{id: 2, amt: 120}, %{id: 3, amt: 80}]

orders |> Enum.filter(&(&1.amt > 50)) |> Enum.map(& &1.id)   # => [2, 3]
Enum.sum_by(orders, & &1.amt)      # 1.18 新增:按函数取值求和
Enum.group_by(orders, &(&1.amt > 50))    # 按分组函数聚成 map

map/filter/reduce/sort_by/group_by 覆盖九成日常需求,全部急切求值。

⚠️ 坑:Enum.at(list, 999_999)、List.last 这类依赖位置的操作要从前走到尾,大列表是 O(n)——需要随机访问换 Map 或 :ets,别硬扛。

Stream(惰性枚举)

1..1_000_000
|> Stream.map(& &1 * 2)          # 此时什么都没算
|> Stream.filter(& rem(&1, 3) == 0)
|> Enum.take(3)                  # 到这里才逐元素流过整条管线

多步变换 + 取前 N 个、或不想为中间结果分配内存时用 Stream;执行靠 Enum.to_list、Enum.take、Stream.run 等收尾函数触发。

⚠️ 坑:管道末尾只有 Stream 变换没有收尾函数,整条管线一行代码都不会执行——”为什么不报错也没结果”十有八九是这个。

for 推导

for n <- 1..5, rem(n, 2) == 0, into: [], do: n * 10   # => [20, 40]

1. 多生成器是笛卡尔积,不是嵌套循环语义
for x <- [1, 2], y <- [:a, :b], do: {x, y}
1. => [{1, :a}, {1, :b}, {2, :a}, {2, :b}]

带过滤条件与 into: 收集目标,适合”筛 + 映射 + 收集”一步写完的场景。

⚠️ 坑:for 是推导式不是循环,没有 break/continue;需要提前退出就说明该用 Enum.take_while 或递归了。

进程与并发

组内共性:BEAM 进程间不共享内存,消息传递是拷贝语义。开山篇用 spawn/receive 打过照面,OTP 篇讲 GenServer 封装,这里记两个最常用条目的速查。

receive 与 after

send(self(), {:ping, 1})

receive do
  {:ping, n} -> n
after
  1_000 -> :timeout    # 1 秒没等到就走这里;没有 after 分支则永久阻塞
end

receive 从进程信箱取消息,after 给等待加超时上限(毫秒)。

⚠️ 坑:信箱里没有匹配形状的消息时,receive 会跳过不匹配的消息继续等(消息留在信箱里),漏配形状的进程看起来”卡死”其实是在等一条永远不来的消息——先 Process.info(self(), :messages) 看信箱里到底有什么。

Task.async / await

task = Task.async(fn -> heavy_work() end)
1. 中间可以做别的事
Task.await(task, 5_000)   # 默认超时 5000 毫秒

一次性并发计算:起一个链接到当前进程的任务,await 收结果。

⚠️ 坑:Task.async 与调用方是链接(link)关系,任务崩了会把父进程一起带崩——不想陪葬用 Task.Supervisor.async_nolink;另外 async 了却从不 await,结果与退出消息会积压在信箱里。

调试与工程

组内共性:Elixir 的调试传统是”插到管道里看一眼”,配合交互式 shell,多数问题不需要断点调试器。

IO.inspect / dbg

"  x  " |> String.trim() |> IO.inspect(label: "after trim") |> String.upcase()
1. after trim: "x"     ——IO.inspect 返回原值,可以插在管道任意一环

dbg(String.upcase(String.trim("  x  ")))   # 1.14+:打印每一步的代码与中间值

IO.inspect 是单点查看,dbg 是整条链路回放。

⚠️ 坑:IO.inspect(x) 作为一行语句调用时返回的是 :ok 不是 x——要插管道必须利用它返回原值的特性;dbg 需要 1.14+,老项目里没有。

iex 三件套与 Mix.install

# iex 里:
1. h Enum.map      查文档;i "abc"      查数据类型信息;recompile()  重编译 mix 项目

Mix.install([:jason])   # 脚本顶部声明依赖,随后直接 Jason.encode!(%{a: 1})
IO.puts("done")

h/i/recompile 覆盖查文档、查值、热重载三件高频事;Mix.install 让单文件脚本也能用 Hex 依赖。

⚠️ 坑:改了项目代码但 iex 里行为没变,是忘了 recompile();Mix.install 只属于脚本与 LiveBook 场景,正式项目依赖声明在 mix.exs,混用会得到不可复现的环境。

速查表

条目 一句话用途 有什么坑
= 与 ^ 形状对齐取数据 失败抛 MatchError;^ 是断言旧值,重绑定是另一回事
Map.get / fetch / fetch! 取值三连,按失败语义选 get 分不清”键不存在”与”值为 nil”
%{s | k: v} 更新 struct/map 已有键 不能新增键,struct 键集合 defstruct 时锁死
插值与 字符串拼装 右侧必须是字符串;字节级截断会切碎 UTF-8
String.length vs byte_size 字素数与字节数 中文按字节截断是半个字符
JSON(1.18+) 内置编解码,包装 OTP 27 JSON decode! 抛异常;1.17 及以前要用 Jason 库
~c”abc” charlist,Erlang 互操作 ‘abc’ != “abc”,单引号不是字符串
Enum 急切枚举全家桶 链表按位置访问是 O(n)
Stream 惰性管线省中间分配 没有收尾函数整条管线不执行
for 筛 + 映射 + 收集一步写完 是推导式不是循环,没有 break
receive / after 收消息 + 超时上限 不匹配的消息留在信箱,等不到时先查信箱
Task.async / await 一次性并发任务 默认 5 秒超时;崩了连带父进程(要隔离用 async_nolink)
IO.inspect / dbg 管道插桩与链路回放 单独调用返回 :ok;dbg 是 1.14+
iex h/i/recompile 查文档/查值/热重载 忘 recompile 以为代码没生效
Mix.install 单文件脚本用 Hex 依赖 只属于脚本场景,项目依赖在 mix.exs

下一步

基础层查完,往上两层:并发与容错的条目(GenServer 回调、Supervisor 策略、Registry)在《OTP 与进程》里按同一套三段式展开;Web 层的条目(LiveView 的 assign/stream、路由)结合实战看《用 Phoenix LiveView 做一个实时看板》。开山篇(Elixir 与 Phoenix 入门)仍是整个系列的定位入口。

版本核实(以官方文档为准):稳定线为 Elixir 1.18.x(1.18.0 发布于 2024-12-19,内置 JSON 模块与 JSON.Encoder 协议、Enum.sum_by、mix format –migrate 为 1.18 特性;1.18.3 发布于 2025-03-06),运行时对应 Erlang/OTP 27 线;dbg 自 1.14、Mix.install 自 1.12、Task 与 receive/after 为早期即有的稳定 API;Task.await 默认超时 5000 毫秒、Supervisor 默认重启强度以官方文档为准。本文未引用后续尚未发布的版本特性。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇