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

走进嵌套数据

从 map 里的 list 取值,再安全地更新深处数据。

01 · 先看完整例子

读取并更新书院记录

先读标题,再观察更新前后是两份数据。

Elixir
student = %{
  name: "阿青",
  courses: [%{title: "Elixir", points: 8}]
}

# 路径先进入 courses,再取第 0 项和 title
get_in(student, [:courses, Access.at(0), :title])

# 原数据不变,updated 是一份新数据
updated =
  update_in(student, [:courses, Access.at(0), :points], fn points ->
    points + 1
  end)

get_in(updated, [:courses, Access.at(0), :points])
先对照结果
  • 第一次得到 "Elixir"。
  • 最后得到 9;student 里的积分仍是 8。
02 · 回头拆代码

从第一行往下读

  1. :courses 进入外层 map 的课程 list。
  2. Access.at(0) 取第一门课,:points 再取积分。
  3. 更新会返回新结构,不会暗中改掉旧变量里的值。
03 · 认清新符号

符号不是暗号

data.key

读取 atom 键;键不存在时会报错。

get_in(data, path)

沿路径读取嵌套值。

update_in(data, path, fun)

沿路径找到旧值,再用函数生成新值。

04 · 代码里的概念

把名字和意思对上

01

嵌套集合

集合里面继续放集合,如学生 map 里再放课程 list。

02

访问路径

从外到内的一串位置。它告诉函数沿哪些键或下标寻找。

05 · 再说清楚

这些写法为什么有用

真实数据常是一层套一层:map 里有 map,也有 list。别一次看完整棵树,先沿一条路走。

get_in/2 按路径取值。update_in/3 找到同一路径后,把旧值交给函数。

路径中的 atom 对应 map 的键,数字对应 list 的位置。list 的第一个位置是 0。

06 · 自己改一次

合上答案,动手

把第二颗星的名字从 "北辰" 改为 "南山"。

练习起点
sky = %{stars: [%{name: "启明"}, %{name: "北辰"}]}

put_in(sky, [:stars, Access.at(___), :name], "南山")

目标结果: 新 map 的第二颗星名是 "南山"。

卡住了,再看提示

第二项的下标是 1。

运行过以后,再看一种答案
一种答案
sky = %{stars: [%{name: "启明"}, %{name: "北辰"}]}

# list 从 0 开始计数,第二项下标是 1
put_in(sky, [:stars, Access.at(1), :name], "南山")
想一想:`update_in/3` 会直接改掉原 map 吗?

不会。Elixir 数据不可变;它返回包含新值的新结构。

带走

记住三句

  1. 1

    读嵌套数据时,一层一层走。

  2. 2

    路径可同时经过 map 和 list。

  3. 3

    更新会产生新数据。

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