模块 02查看课程目录
00起跑线:两门语言,一台机器01先建立 BEAM 心智模型02Elixir 基础:让数据流过函数03Erlang 基础:读懂 BEAM 的母语04两种语法,一套语义05从裸进程理解并发06从手写循环推导 OTP07监督树与应用08状态、吞吐与背压09分布式与可运维性10Erlang ↔ Elixir 互操作11综合项目:可靠任务调度器
02
语言入门Elixir

Elixir 基础:让数据流过函数

模式匹配、多子句函数、Enum 与管道,不是四个孤立语法点。

5检查点
6 小时预计完成
为什么先学这个

先修正心智模型,再增加 API

Elixir 最容易被写成带管道符的命令式代码:到处 if、在一条长管道里混合解析与副作用。更稳的做法是先用模式匹配定义数据形状,再让每个小函数只承担一个转换。

学完你能做到

可验证的学习目标

  • 能使用模式匹配、guard 与多子句函数表达分支
  • 能在 Enum 与 Stream 之间做有理由的选择
  • 能为纯函数编写最小 ExUnit 测试
前置知识
  • 完成 BEAM 心智模型
  • 知道 list 与 map 的用途
最小心智模型

先抓住三个词

01

模式匹配

`=` 不是普通赋值。它要求右侧数据符合左侧形状,并把未绑定变量绑定到对应位置。

02

多子句函数

同名同元数的函数可按模式和 guard 分成多条路径,调用时从上到下匹配。

03

管道

`|>` 把左侧结果作为右侧函数的第一个参数;它改善数据流可读性,但不会自动改善函数边界。

双语代码桥

先对齐协议,再看标点

先看 Elixir 主实现,再用右侧 Erlang 轮廓确认:真正共享的是模式与数据,不是标点。

Elixir
defmodule LogSummary do
  def summarize(lines) do
    lines
    |> Stream.map(&String.trim/1)
    |> Stream.reject(&(&1 == ""))
    |> Enum.map(&parse_line/1)
    |> Enum.frequencies_by(& &1.level)
  end

  defp parse_line("ERROR " <> message),
    do: %{level: :error, message: message}

  defp parse_line("INFO " <> message),
    do: %{level: :info, message: message}
end
Erlang
%% 同一数据流的 Erlang 轮廓
summarize(Lines) ->
  Clean = [string:trim(L) || L <- Lines, L =/= <<>>],
  Parsed = [parse_line(L) || L <- Clean],
  frequencies(Parsed).

parse_line(<<"ERROR ", Message/binary>>) ->
  #{level => error, message => Message};
parse_line(<<"INFO ", Message/binary>>) ->
  #{level => info, message => Message}.
LAB
可运行实验

管道顺序为什么是语义,不是排版

约 15–25 分钟

给解析器加入空行和前后空格。交换 trim 与 reject 的顺序,看一条看似无害的调整如何改变结果。

  1. 01

    准备 `[' INFO boot ', ' ', 'ERROR timeout']` 对应的 Elixir 字符串列表

  2. 02

    运行原顺序并记录频次

  3. 03

    把 reject 移到 trim 之前,再运行同一输入

在终端 / shell 中运行
mix test --trace
预期观察
  • 先 trim 再 reject 时,纯空格行被删除
  • 先 reject 再 trim 时,纯空格行会进入解析器
故意弄坏

把兜底 `parse_line/1` 删掉,再传入 `WARN slow`。观察 FunctionClauseError 中保留了哪些定位信息。

这个实验能证明

数据转换顺序会改变可匹配的数据集合。

这个实验不能证明

这个小样本不能证明 Stream 一定更快;是否受益取决于数据量、终止条件与后续消费方式。

快速自测

什么时候优先使用 Stream 而不是 Enum?

本章挑战

命令行日志分析器

实现一个 Mix 项目:读取日志文件,忽略空行,统计 INFO/WARN/ERROR,并列出出现次数最多的三个消息。

提示 1轻推一下

先让解析函数保持纯净,再把文件 I/O 放到边界。

提示 2缩小问题

为未知级别返回 `{:error, line}`,不要悄悄丢弃。

提示 3接近实现

至少写一个带空格、一个未知级别、一个空文件测试。

提示 4用验收标准反推

从下面每一条验收标准倒推一个最小测试。若某条无法写成测试,先把表述改成可观察结果。

完成标准
  • 解析与文件读取分离
  • 未知行有显式错误结果
  • ExUnit 覆盖正常、边界与错误路径
带走这三句话

复习卡

  1. 1先定义数据形状,再定义转换流水线。
  2. 2管道只是参数传递语法;好设计仍依赖小而清晰的函数。
  3. 3Stream 是惰性工具,不是“更快版 Enum”。
继续核对

本章一手资料

Elixir 基础类型EnumExUnit