综合测验 + 代码练习
8 道读代码 + 2 道改代码 + 5 道综合题。检验你 0001-0004 是否掌握。
**预计时间:**60-90 分钟。
如果 > 80% 正确:你已经掌握 Elixir,可以开始改
ex/elixir 代码。如果 60-80% 正确:回去精读 0001 的"模式匹配 + Enum"和 0002 的"GenServer"两段。
如果 < 60% 正确:建议从头读 0001,并配 IEx 边读边跑。
一、读代码题(8 道)
每道给一段来自 ex/elixir 真实代码,问它做了什么 / 输出了什么。
R1. Pipeline.Repo 的 schema_prefix 是?
R2. 下列代码的输出?
R3. upsert_messages([]) 的返回值?
R4. Pipeline.Web 是基于什么库的?
R5. 下面 edges() 函数的输出类型?
R6. Pipeline.Llm 调 Pipeline.Llm.Real 还是 Pipeline.Llm.Dummy?
R7. recent_runs() 里 Ecto.Adapters.SQL.query 调的是什么?
R8. Pipeline.Jobs.NewsDigest 用 use Oban.Worker 后,perform/1 接收的 job 参数类型?
二、改代码题(2 道)
用 IEx 或者直接改文件做。
filter_and_sum/2:- 接收一个 list 和一个数字
n - 返回 list 中所有 大于 n 的元素的 平方和
- 用管道 + Enum 链
- 在 IEx 里测试:
filter_and_sum([1, 2, 3, 4, 5], 2)应返回50(9+16+25)
参考解(不要先看):
def filter_and_sum(list, n) do
list
|> Enum.filter(&(> &1, n)) # 或 &(&1 > n)
|> Enum.map(&(&1 * &1))
|> Enum.sum()
end
# 验证:filter_and_sum([1, 2, 3, 4, 5], 2) = 9 + 16 + 25 = 50 ✓
ex/elixir/lib/pipeline/jobs/news_pull.ex,现在它是:defmodule Pipeline.Jobs.NewsPull do use Oban.Worker, queue: :feishu, max_attempts: 3 def perform(_job), do: :ok end 要求:把它改成一个"调用飞书 API 拉消息"的工作流——先返回
Pipeline.NewsStore.upsert_messages/1 入库的列表(如果传入了 args),否则 :ok。提示:
- 看
Pipeline.NewsStore.upsert_messages/1的签名 - 看
Pipeline.Jobs.NewsDigest.run/1怎么用Map.get(args, "key", default) - 先写一个能编译通过的最小实现,不要真去调飞书 API
参考解(不要先看):
defmodule Pipeline.Jobs.NewsPull do
use Oban.Worker, queue: :feishu, max_attempts: 3
require Logger
def perform(%Oban.Job{args: args}), do: run(args)
def perform(args) when is_map(args), do: run(args)
defp run(%{"messages" => msgs}) when is_list(msgs) do
{:ok, run_id} = Pipeline.Execution.start("news_pull")
inserted = Pipeline.NewsStore.upsert_messages(msgs)
Pipeline.Execution.finish(run_id, "done", inserted)
{:ok, inserted}
end
defp run(_), do: :ok
end
三、综合题(5 道)
不基于具体代码,问”为什么这样设计” / “什么时候用”。
C1. Elixir 项目为什么用 on_conflict: :nothing 做"幂等去重"?这跟其他语言"先查后插"有什么区别?
C2. 为什么 Pipeline.Application 用 if ... do ... else ... end 动态加子进程到监督树?
C3. Pipeline.Llm 抽象的"行为 + 配置驱动实现"模式,相比直接 defimpl 用协议的优势是?
C4. 为什么 BEAM 比 Java 适合"百万并发长连接"?
C5. ex/elixir 项目为什么不直接用 n8n 而要自己用 Elixir 实现?
四、自检清单
全部 ✓ 表示你可以开始改 ex/elixir 真实代码了:
- [ ] 知道
{:ok, x}/{:error, e}是 Elixir 的”标准返回”模式 - [ ] 能读懂
case result do {:ok, v} -> ...; {:error, e} -> ... end - [ ] 能用管道
|>链式调用Enum.map/Enum.filter/Enum.sum - [ ] 能看懂
use Oban.Worker、use Plug.Router、use Ecto.Schema都在做什么 - [ ] 知道 GenServer 是什么(Oban Worker 的内部实现)
- [ ] 知道 Supervisor 重启策略
:one_for_one的语义 - [ ] 知道
Application.get_envvscompile_env的区别 - [ ] 知道协议 + 行为是两套不同的多态
- [ ] 知道 BEAM 进程 vs OS 进程 / goroutine 的差异
- [ ] 知道为什么 Elixir 适合 99.999% 可用性 / 长连接场景
- 读 extensions.md 的 "速通建议"段,按你兴趣挑 1-2 个 🟡 主题深入
- 打开 IEx(
iex -S mix)跑Pipeline.Dag.nodes/0、Pipeline.Dag.edges/0看看 - 在
ex/elixir/lib/pipeline/jobs/news_pull.ex真正实现 C2 的飞书拉取 - 看 anki.md 把核心概念制成 Anki 卡片复习
**课程结束。**回到 README.md 查看完整索引,或直接看 extensions.md 开始扩展学习。