Elixir 从零 · 第 08 课(展开目录)
01 · 先让代码说话02 · 先认六种值03 · 把值装起来04 · 从旧 list 生成新 list05 · 让形状对上06 · 用 case 做选择07 · 把文字安全变成整数08 · 拆开 &1 和管道09 · 把代码放进项目10 · 真假不只靠 true11 · 走进嵌套数据12 · 一字不一定一字节13 · 让函数按形状接活14 · 把一列数收成一个值15 · 给不同问题选不同路16 · 给 map 一张名片17 · 让项目自己验答案18 · 让成功和失败长得一样19 · 读写文件先管好路20 · 只算眼前需要的21 · 同一句话,不同做法22 · 先约好模块会做什么23 · 把数据形状写在门口24 · 测试不只看一条好路25 · 把项目磨成一个命令26 · 定下小项目边界27 · 把真实文字洗干净28 · 只开三扇门29 · 把约定钉在代码旁30 · 打包,也留下脚印31 · 照任务书交卷
ELIXIR · 试跑 · LESSON 0835 分钟
拆开 &1 和管道
先写完整匿名函数,再读 capture、Enum 和 `|>`。
01 · 先看完整例子
整段复制:从长写法到短写法
三段代码都在“把函数交给另一个函数”。
Elixir
# 先写完整:number 是每次收到的值
long_double = fn number -> number * 2 end
Enum.map([1, 2, 3], long_double)
# 再看简写:&1 仍是每次收到的值
Enum.map([1, 2, 3], &(&1 * 2))
# 取得已有的 String.trim/1 函数
[" 桃", "李 "] |> Enum.map(&String.trim/1)
|> Enum.join("、")先对照结果
- 前两段都得到
[2, 4, 6]。 - 最后一条管道得到
"桃、李"。 &1只在 capture 简写内部有意义。
02 · 回头拆代码
从第一行往下读
Enum.map/2接收两个参数:一个集合和一个函数,所以名字末尾是/2。&(&1 * 2)等于fn number -> number * 2 end。第一次见时主动还原成长写法。&String.trim/1没有马上调用trim,只是把这个函数交给Enum.map/2。- 最后一行等于
Enum.join(上一步结果, "、")。管道只自动填第一个参数。
03 · 认清新符号
符号不是暗号
&(&1 * 2)创建匿名函数;&1 是收到的第一个参数。完整写法是 fn number -> number * 2 end。
&String.trim/1取得已经存在的 String.trim/1 函数,稍后交给别的函数调用。
|>把左边结果送到右边函数的第一个参数位置。
04 · 代码里的概念
把名字和意思对上
capture
& 可以取得一个已有函数,也可以创建一个简写匿名函数。
Enum.map/2
依次把集合中的每个值交给函数,收集每次返回的新值。
管道
|> 把左边结果作为右边函数的第一个参数。
05 · 再说清楚
这些写法为什么有用
&1 不是一个独立变量。它只在由 & 开始的 capture 简写表达式中有意义,表示匿名函数收到的第一个参数。
短写法应该最后学。先读懂 fn number -> number * 2 end,再把它缩成 &(&1 * 2)。
Enum 里的函数会依次处理集合。管道 |> 把左边结果放进右边函数的第一个参数位置。
设计选择
为什么这样写
为什么把数据一站一站传下去?
管道把一长串嵌套调用改成从上往下的数据旅程。每一步仍应是能单独理解和测试的小函数。
管道不是更高级的循环。若参数位置别扭、步骤偷偷做大量 IO,拆成普通调用反而更清楚。
换一把尺子
从 Java、Python、JavaScript 看过来
Java
- 熟悉的起点
- Stream 链或连续方法调用。
- BEAM 在意什么
- Elixir
|>只是把左值放进下一次调用;Erlang 通常显式嵌套或给中间值命名。 - 别带错直觉
Enum多半立即计算,Stream才是惰性。
Python
- 熟悉的起点
- 生成器、推导式和逐步赋值。
- BEAM 在意什么
- capture 与 pipe 把小函数组合起来,但不会原地改 list。
- 别带错直觉
&String.trim/1是函数值,String.trim/1只是函数身份写法。
JavaScript
- 熟悉的起点
- map/filter 链、回调或 iterator。
- BEAM 在意什么
- 数据默认是不可变值;每一步交回一个新结果。
- 别带错直觉
- 不要把
&1读成 JS 的位运算与号。
06 · 自己改一次
合上答案,动手
先用完整匿名函数和 &1 把 [2, 3, 4] 每项加 1,再补出管道,清理两段文字并连接。
练习起点
# 第一遍:写完整名字
Enum.map([2, 3, 4], fn number -> ____ end)
# 第二遍:把收到的第一个参数写成 &1
Enum.map([2, 3, 4], &(____))
# 第三遍:亲手补上两段管道
[" 桃", "李 "]
____ Enum.map(&String.trim/1)
____ Enum.join("、")目标结果: 前两段都得到 [3, 4, 5],最后得到 "桃、李"。
卡住了,再看提示
前两空分别写 number + 1 和 &1 + 1;管道两处都写 |>。
运行过以后,再看一种答案
一种答案
# 长写法先把输入的名字写清楚
Enum.map([2, 3, 4], fn number -> number + 1 end)
# 短写法里 &1 表示第一个参数
Enum.map([2, 3, 4], &(&1 + 1))
# 管道把上一步结果送到下一个函数
[" 桃", "李 "]
|> Enum.map(&String.trim/1)
|> Enum.join("、")想一想:`&String.trim/1` 会立刻把某个字符串的空格删掉吗?
不会。它只是取得这个函数。等 Enum.map/2 把每个字符串交给它时,函数才会被调用。
带走
记住三句
- 1
先读长写法,再接受短写法。
- 2
&1是 capture 简写中的第一个参数。 - 3
管道把左边结果放到右边的第一个参数。
本课结束代码也跑过一次,就把这一课收好。