Elixir 从零 · 第 04 课(展开目录)
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 0420 分钟

从旧 list 生成新 list

先绑定一列值,再用 for comprehension 产生新 list。

01 · 先看完整例子

保留旧 list,产生新 list

先看输入 list,再看 comprehension 产生的结果。

Elixir
# numbers 指向原来的 list
numbers = [1, 2, 3]

# 每次结果会被收集到新 list
plus_one =
  for number <- numbers do
    number + 1
  end

{numbers, plus_one}
先对照结果
  • 得到 {[1, 2, 3], [2, 3, 4]}。
  • numbers 仍是 [1, 2, 3]。
  • plus_one 指向新 list [2, 3, 4]。
02 · 回头拆代码

从第一行往下读

  1. numbers = [1, 2, 3] 把名字绑定到原 list。
  2. number <- numbers 从左到右取出 1、2、3,当前值暂时叫 number。
  3. 块里的 number + 1 每次产生一个新数字,for 把它们收集成 [2, 3, 4]。
  4. 整个过程没有修改 numbers,所以 tuple 中仍能看到原 list。
  5. IEx 只负责把结果展示出来。看到 ~c"..." 时,可以逐个对照字符码点;值依旧是整数 list。
03 · 认清新符号

符号不是暗号

=

这里先读作“让左边名字指向右边的值”。下一课会看到它也是匹配运算符。

<-

从右边集合依次取一个值,交给左边名字。

for ... do ... end

运行 comprehension,并把每次结果收集成新 list。

+

把左右两个数字相加,返回一个新数字。

~c"4L["

charlist 写法;它与 [52, 76, 91] 是同一个值。

04 · 代码里的概念

把名字和意思对上

01

绑定

让一个名字指向某个值,如 count = 3。

02

不可变

已经存在的值不会被原地改写。计算会产生新值。

03

comprehension

从集合依次取值,计算后收集成新集合;它不会修改原集合。

04

charlist 显示

charlist 本质上仍是整数 list。整数都能解释成可打印字符时,IEx 会优先用 ~c"..." 显示。

05 · 再说清楚

这些写法为什么有用

名字让代码容易读。numbers = [1, 2, 3] 可以读成“名字 numbers 现在指向这列数字”。

Elixir 的数据不会被原地改掉。对 list 做计算会产生新 list,原来的 list 仍保持不变。

for value <- list do ... end 叫 comprehension。它从 list 依次取值,用块里的表达式产生新值,最后收集成新 list。

练习里的 52、76、91 恰好也是字符 4、L、[ 的 Unicode 码点。因此 IEx 可能把同一个整数 list 显示成 ~c"4L["。这只是显示方式,不是数据变成了字符串。

单独输入空行或只有 # 注释的一行时,IEx 会回显 nil。它只表示这一行没有产生有用结果,不会被放进 scores 或 raised。

06 · 自己改一次

合上答案,动手

把分数 [52, 76, 91] 每项加 5,同时保留原 list。

练习起点
# 原始分数
scores = [52, 76, 91]

# 补上取值符号和每次产生的新值
raised =
  for score ____ scores do
    ____
  end

{scores, raised}

目标结果: 逻辑结果是 {[52, 76, 91], [57, 81, 96]}。IEx 也可能显示成 {~c"4L[", ~c"9Q"},两种写法表示完全相同的两个整数 list。粘贴时夹在中间的 nil` 只来自空行或注释。

卡住了,再看提示

取值使用 <-;块里写 score + 5。若想强制按数字显示,可先单独运行 IEx.configure(inspect: [charlists: :as_lists])。

运行过以后,再看一种答案
一种答案
# 原始 list 保持不变
scores = [52, 76, 91]

# comprehension 收集每次加 5 的结果
raised =
  for score <- scores do
    score + 5
  end

{scores, raised}
想一想:IEx 显示 `scores` 为 `~c"4L["`,是否说明它已经变成文字?

没有。~c"4L[" == [52, 76, 91] 的结果是 true。charlist 就是整数 list 的另一种显示法;for 也没有修改原来的 scores。

带走

记住三句

  1. 1

    名字指向值,方便后面的代码使用。

  2. 2

    <- 从集合中依次取值。

  3. 3

    comprehension 产生新 list,不会原地修改旧 list。

  4. 4

    ~c"..." 可能只是 IEx 对整数 list 的显示选择。

本课结束代码也跑过一次,就把这一课收好。