# 惰性序列 `map`、`filter`、`take`、`drop` 和 `mapcat` 在接收集合参数时返回惰性序列。在你消费结果之前,计算不会执行。这让针对大型或无限输入的流水线保持高效。 ## 什么是惰性? 惰性序列按需计算其元素。下面的链式调用不会遍历整个输入,只计算终止操作所需的部分:
@{ 1 2 3 4 5 6 7 8 9 10 }
    |> filter { ($it % 2) == 0 }
    |> map { $it * 10 }
    |> take 2
    |> collect
# => @{ 20 40 }
# only the first two even numbers were mapped
上面的流水线一旦 `take 2` 取得两个值就立即终止。即使源是无限的,该流水线也仍能完成。 ## 返回惰性序列的操作 | 操作 | 1 参数(转换器) | 2 参数(惰性序列) | |---|---|---| | `map` | `[map f]` | `[map $xs f]` 或 `$xs \|> map f` | | `filter` | `[filter pred?]` | `[filter $xs pred?]` 或 `$xs \|> filter pred?` | | `take` | `[take n]` | `[take $xs n]` 或 `$xs \|> take n` | | `drop` | `[drop n]` | `[drop $xs n]` 或 `$xs \|> drop n` | | `mapcat` | `[mapcat f]` | `[mapcat $xs f]` 或 `$xs \|> mapcat f` | 1 参数形式返回转换器(参见 转换器)。 ## 惰性序列的求值 惰性序列不能直接打印。使用终止操作强制求值:
set xs @{ 1 2 3 4 5 }

$xs |> map { $it * 2 } |> collect             # force to list
$xs |> filter even? |> count                  # count of matches
$xs |> map { $it * 2 } |> reduce 0 { $acc + $it }   # fold
$xs |> filter { $it > 2 } |> find { $it > 3 }  # first match
$xs |> map { $it * 2 } |> join ","            # stringify
$xs |> filter { $it > 2 } |> first            # same as `find` without pred
$xs |> filter { $it > 2 } |> last
### TODO 隐式求值 `for` 循环、`echo` 和赋值(`set`)会自动对惰性序列求值:
for x [map @{1 2 3} { $it * 2 }] { echo $x }    # realises via for-loop
echo $xs |> map { $it * 2 }                       # realises via echo
set result $xs |> map { $it * 2 }                 # realises on assignment
## 流水线与嵌套调用 对于多步转换,流水线是惯用形式:
# ✓ Preferred
@{ 1 2 3 } |> map { $it * 2 } |> filter { $it > 2 } |> collect

# ✗ Works, but unidiomatic
[filter [map @{ 1 2 3 } { $it * 2 }] { $it > 2 }]
流水线形式让每一步从左到右阅读,避免向右漂移。 ## 步骤融合 流水线步骤在运行时融合——求值过程只遍历源一次,并对每个元素运行所有转换。不会分配中间集合。
@{ 1 2 3 4 5 }
    |> map { $it * 2 }
    |> map { $it + 1 }
    |> filter { $it > 4 }
    |> collect
# one walk, two maps and a filter per element
## 缓存 惰性序列的元素一旦被强制求值,就会被缓存。重新遍历同一结果不会重新运行流水线:
set xs [map @{ 1 2 3 } { expensive-op $it }]
echo [$xs first]                              # runs expensive-op on 1
echo [$xs first]                              # uses cached result
## 后缀语法糖 管道操作符是最自然的形式,但单步调用也可以在变量上使用后缀形式:
set xs @{ 1 2 3 }
set doubled $xs(map { $it * 2 })
set evens  $xs(filter { ($it % 2) == 0 })
注意:变量与左括号之间没有空格。`$xs (map ...)` 是语法错误。 ## 迭代映射与字符串 `ISeq` 适用于任何序列,而不仅是列表。映射产生 `@{key value}` 对;字符串产生单个字符:
%{ :a 1 :b 2 } |> map { $it(1) } |> collect    # => @{ 1 2 }
"hello" |> map { $it(upper) } |> join ""      # => "HELLO"
更多内容参见 集合。 ## 使用 `take` 提前终止 `[take n]` 是终止无限或超大型流水线的主要工具:
def naturals [iterate { $it + 1 } 1]          # if you have iterate

# Without iterate, use range with a large bound and take what you need
[range 0 1000000] |> filter { ($it % 7) == 0 } |> take 5 |> collect
# => @{ 0 7 14 21 28 }
## 何时改用转换器 惰性序列形式非常适合一次性的集合到集合转换。在以下情况下应改用转换器(参见 转换器): - 同一流水线要应用于多个集合。 - 汇聚端不是向量(集合、映射、求和、自定义 reducer)。 - 希望借助 `%Reduced` 在整个链上显式提前终止。 ## 快速参考 | 操作 | 返回值 | 说明 | |---|---|---| | `map` | 惰性序列(2 参数) | 对每个元素应用 f | | `filter` | 惰性序列(2 参数) | 保留 pred 为真的元素 | | `take` | 惰性序列(2 参数) | 前 n 个元素 | | `drop` | 惰性序列(2 参数) | 除前 n 个之外的所有元素 | | `mapcat` | 惰性序列(2 参数) | 映射后拼接结果 | | `collect` | 列表 | 强制求值惰性序列 | | `reduce` | 折叠后的值 | 使用初始值和步进函数进行归约 | | `find` | 第一个匹配或 nil | 查找第一个满足条件的元素 | | `any?` / `all?` | 布尔值 | 存在性 / 全称性 | | `group` | 以结果为键的映射 | 按函数分区 | | `count` | 数字 | 求值后的长度 | | `first` / `last` | 元素 | 两端 | | `sort` | 列表 | 排序后的副本 | | `unique` | 列表 | 去重 |