Learning
VOL. XIII · NO. 10 · Elixir · 01 JAN 1970

综合测验 + 代码练习

Elixir 编程 · 01 JAN 1970 · 4 min read · 910 words
· · ·

8 道读代码 + 2 道改代码 + 5 道综合题。检验你 0001-0004 是否掌握。

**预计时间:**60-90 分钟。

做完再回看
本节不要回看前 4 课。做完再对答案。
如果 > 80% 正确:你已经掌握 Elixir,可以开始改 ex/elixir 代码。
如果 60-80% 正确:回去精读 0001 的"模式匹配 + Enum"和 0002 的"GenServer"两段。
如果 < 60% 正确:建议从头读 0001,并配 IEx 边读边跑。

一、读代码题(8 道)

每道给一段来自 ex/elixir 真实代码,问它做了什么 / 输出了什么。

R1

R1. Pipeline.Repo 的 schema_prefix 是?

R2

R2. 下列代码的输出?

R3

R3. upsert_messages([]) 的返回值?

R4

R4. Pipeline.Web 是基于什么库的?

R5

R5. 下面 edges() 函数的输出类型?

R6

R6. Pipeline.LlmPipeline.Llm.Real 还是 Pipeline.Llm.Dummy

R7

R7. recent_runs()Ecto.Adapters.SQL.query 调的是什么?

R8

R8. Pipeline.Jobs.NewsDigestuse Oban.Worker 后,perform/1 接收的 job 参数类型?

二、改代码题(2 道)

用 IEx 或者直接改文件做。

C1. 写一个函数
写一个函数 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 ✓
C2. 改一个 Oban Worker
打开 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

C1. Elixir 项目为什么用 on_conflict: :nothing 做"幂等去重"?这跟其他语言"先查后插"有什么区别?

C2

C2. 为什么 Pipeline.Applicationif ... do ... else ... end 动态加子进程到监督树?

C3

C3. Pipeline.Llm 抽象的"行为 + 配置驱动实现"模式,相比直接 defimpl 用协议的优势是?

C4

C4. 为什么 BEAM 比 Java 适合"百万并发长连接"?

C5

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.Workeruse Plug.Routeruse Ecto.Schema 都在做什么
  • [ ] 知道 GenServer 是什么(Oban Worker 的内部实现)
  • [ ] 知道 Supervisor 重启策略 :one_for_one 的语义
  • [ ] 知道 Application.get_env vs compile_env 的区别
  • [ ] 知道协议 + 行为是两套不同的多态
  • [ ] 知道 BEAM 进程 vs OS 进程 / goroutine 的差异
  • [ ] 知道为什么 Elixir 适合 99.999% 可用性 / 长连接场景
下一步建议
  • extensions.md 的 "速通建议"段,按你兴趣挑 1-2 个 🟡 主题深入
  • 打开 IEx(iex -S mix)跑 Pipeline.Dag.nodes/0Pipeline.Dag.edges/0 看看
  • ex/elixir/lib/pipeline/jobs/news_pull.ex 真正实现 C2 的飞书拉取
  • anki.md 把核心概念制成 Anki 卡片复习

**课程结束。**回到 README.md 查看完整索引,或直接看 extensions.md 开始扩展学习。