指南 · 第 3 章
Flow 与 Stream
理解管道传值、惰性流和单次消费语义。
Pipe 如何传值
Pipe 是普通函数调用的组合规则:x |> f 等价于 f(x),x |> f(a) 等价于 f(x, a),x |> obj.f(a) 等价于 obj.f(x, a)。List、Map 和 Range 须显式调用 stream 后再使用流算子。它不会自动把标量变成 Stream、展开嵌套 Stream、访问 it 字段、忽略错误、字符串化值或启动 Shell。
[1, 2, 3, 4, 5]
|> stream
|> map { number -> number * 2 }
|> where { number -> number > 5 }
|> take(2)
|> printStream 的生命周期
Stream 是惰性、拉取式、单次消费序列。创建管道只组合 operator;终端开始拉取时,上游才逐项产生数据。生命周期是 open → next* → close,正常结束、take 提前停止、错误和取消都会从下游向上游关闭资源。
| 阶段 | 发生的事情 |
|---|---|
| 创建 | Source 返回 Stream,但尚未读取数据 |
| 组合 | map、where 等 operator 连接成 Pipeline |
| 消费 | print、collect、save 等终端开始逐项拉取 |
| 关闭 | 完成、提前停止、Error 或取消释放上游资源 |
逐项、过滤与观察算子
[5, 2, 5, 1, 3]
|> stream
|> skip(1)
|> take(4)
|> inspect { number -> print("seen", number) }
|> where { number -> number >= 3 }
|> map { number -> number * 10 }
|> distinct
|> collect
|> print函数map#
map(Stream<T>, Function(T -> U)) -> Stream<U>惰性地对每项调用闭包,一项输入对应一项输出,不自动展开。
函数where#
where(Stream<T>, Function(T -> Bool)) -> Stream<T>惰性保留闭包返回 true 的项目;闭包必须返回 Bool。
函数take#
take(Stream<T>, Int) -> Stream<T>惰性保留前 n 项,达到数量后提前关闭上游。
函数skip#
skip(Stream<T>, Int) -> Stream<T>惰性丢弃前 n 项,然后传递其余项目。
函数inspect#
inspect(Stream<T>, Function(T -> Value)) -> Stream<T>为每项执行观察闭包,再原样传递项目。
函数distinct#
distinct(Stream<Hashable>) -> Stream<Hashable>惰性去除重复的可 Hash 标量,并保存已见集合。
map 与 flat_map 的区别
map 的闭包返回什么,下游就收到什么。如果返回 Stream,结果是 Stream<Stream<T>>。flat_map 要求闭包返回 Stream,并把每个子流依次展开成一条 Stream。
let batches = [[1, 2], [3, 4]]
batches
|> stream
|> flat_map { batch -> batch |> stream }
|> printBarrier 和终端算子到底做什么
逐项算子只需保存当前项;Barrier 必须先看完或保存大量输入才能产生正确结果。sort_by 要保存全部输入后排序,group_by 要保存每组的全部 values,collect 把全部项组成 List,reduce/count/sum 等终端算子读取到结束才返回标量。它们都受 max_memory、集合大小和运行时间限制。
let ordered = [5, 1, 3, 2, 4]
|> stream
|> sort_by({ order: "asc" }) { number -> number }
|> collect
let grouped = [
{ team: "core", name: "Ada" },
{ team: "web", name: "Linus" },
{ team: "core", name: "Grace" }
]
|> stream
|> group_by { person -> person.team }
|> collect
print(ordered)
print(grouped)函数sort_by#
sort_by(Stream<T>, Map, Function(T -> Comparable)) -> Stream<T>物化有限输入,按闭包 key 和 asc/desc 选项稳定排序。
函数group_by#
group_by(Stream<T>, Function(T -> Hashable)) -> Stream<Group<T>>物化有限输入并按 Hash key 输出 Group;Group 含 key 与 values。
函数collect#
collect(Stream<T>) -> List<T>消费有限 Stream 并物化为 List。
函数reduce#
reduce(Stream<T>, U, Function(State<T,U> -> U)) -> U以 initial 累积 Stream;闭包接收含 acc/item/index 的 state。
函数count#
count(Stream<T>) -> Int消费 Stream 并返回项目数。
函数sum#
sum(Stream<Number>) -> Number消费数值 Stream 并求和,遵守 Int 溢出规则。
函数min#
min(Stream<Number>) -> Number | Null消费数值 Stream,返回最小值或空流的 null。
函数max#
max(Stream<Number>) -> Number | Null消费数值 Stream,返回最大值或空流的 null。
函数first#
first(Stream<T>) -> T | Null返回第一项或 null,并提前关闭上游。
函数last#
last(Stream<T>) -> T | Null消费 Stream 并返回最后一项或 null。
函数any#
any(Stream<T>, Function(T -> Bool)) -> Bool任一项目满足谓词即返回 true,并短路关闭上游。
函数all#
all(Stream<T>, Function(T -> Bool)) -> Bool所有项目满足谓词才返回 true;首个 false 时短路。
副作用、错误与并发
只有终端或 Action 才会真正消费 Pipeline。print、for_each、save_*、run 和 send 会执行输出、文件、进程或网络操作。普通 Error 默认终止 Pipeline;需要逐项保留失败时使用 attempt,需要替换整条失败上游时使用 on_error。
parallel(n) 使用隔离 worker 并保持输出顺序。并发数量、可发送值和取消语义在“并发与监听”章节展开。
