Files
playbook/skills/tsl-syntax-reference/references/12_matrix_and_collections.md
T

357 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TSL 数组、键表、矩阵样数据与集合运算
文档类型:语法主线
是否可直接用于生成代码:是
是否含可直接照写示例:是
是否含不可照写反例:否
这一篇收拢 `array(...)` 的扩展用法:顺序数组、字符串键表、嵌套数组、本页明确的矩阵样比较,以及 `in` / `sqlin` / `union2` / `intersect` / `minus` / `outersect` 这类去重型集合关系。
## 本篇职责
回答“`array(...)` 在 TSL 里除了最普通的一维数组,还能怎样组织数据;哪些矩阵样写法属于本页文档明确形态;元素、子集、整行和行集合关系应该怎样判断”。
## 智能体数组/矩阵样数据判断流程
1. 先判断需要顺序数组、字符串键表、嵌套数组、矩阵样比较,还是去重型集合关系。
2. 普通容器优先从 `array(...)` 起手;下标和键访问回看值语法页。
3. 矩阵链式比较只照 `::` 系列文档明确示例写,不要混用标量链式比较。
4. 基础成员判断用 `in` / `not in`;整行存在判断用 `sqlin` / `not sqlin`;去重型行集合并、交、差、对称差用 `union2` / `intersect` / `minus` / `outersect`
5. 结果集过滤或 TS-SQL 查询需求分别跳转到 [13_resultset_and_filters.md](13_resultset_and_filters.md) 或 [14_ts_sql.md](14_ts_sql.md)。
6. 没有对应代码块时不要发明数组/矩阵样数据/集合关系写法。
## 核心规则
- `array(...)` 既可以写顺序数组,也可以写字符串键表。
- 顺序数组下标从 `0` 开始;字符串仍然从 `1` 开始。
- `array(...)` 可以继续嵌套,形成二维或矩阵样数据。
- 矩阵链式比较 `::>``::<``::<>``::==``::>=``::<=` 属于本页文档明确形态。
- `in` / `not in` 处理的是元素存在关系,以及左侧为数组时的子集关系。
- `sqlin` / `not sqlin` 处理的是行存在关系;左侧要当成一整行去匹配右侧结果集。
- `union2``intersect``minus``outersect` 都按“行”运算,而不是按单元格逐个运算。
- 集合运算结果会折叠重复行;如果需求是保留重复记录,改看 [13_resultset_and_filters.md](13_resultset_and_filters.md)。
- 当数据本身就是一维数组时,按行集合运算和按元素集合运算是一致的。
- 左侧是数组时,先判定需求语义:子集关系用 `in`,整行存在关系用 `sqlin`
## 可直接照写示例
### 基础数组与键表
顺序数组与字符串键表:
代码块身份:可直接照写示例
```tsl
arr := array(10, 20, 30);
hash := array("Code": "0001", "Price": 12.3);
writeLn("ARR0=", arr[0]);
writeLn("ARR1=", arr[1]);
writeLn("HASH=", hash["Code"]);
```
结果说明:
- `arr[0] = 10`
- `arr[1] = 20`
- `hash["Code"] = "0001"`
嵌套数组:
代码块身份:可直接照写示例
```tsl
[r1, r2] := array((1, 2), (3, 4));
writeLn(r1[0]);
writeLn(r1[1]);
writeLn(r2[0]);
writeLn(r2[1]);
```
结果说明:
- 依次输出 `1``2``3``4`
- 说明 `array((1, 2), (3, 4))` 这种嵌套数组写法可以按普通数组继续读取
代码块身份:输出片段
```text
1
2
3
4
```
### 矩阵样比较
矩阵链式比较:
代码块身份:可直接照写示例
```tsl
r := array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1);
writeLn(r[0]);
writeLn(r[1]);
writeLn(r[2]);
s := array(1, 2, -1) ::< 2 ::< array(3, 2, 1);
writeLn(s[0]);
writeLn(s[1]);
writeLn(s[2]);
```
结果说明:
- `array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1)` 的三个元素依次输出 `1``0``1`
- `array(1, 2, -1) ::< 2 ::< array(3, 2, 1)` 的三个元素依次输出 `1``0``0`
- 说明矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用
### `in`、`not in`、`sqlin`、`not sqlin`
`in` 既可以判断单个元素是否存在,也可以判断左侧数组是否是右侧结果集的子集:
代码块身份:可直接照写示例
```tsl
writeLn(1 in array(1, 2, 2));
writeLn(1 in array(0, 2));
writeLn(1 in array((1), (2)));
writeLn(array(1, 2) in array(1, 2, 3, 4));
writeLn(array(1, 3) in array((1, 2), (3, 4)));
writeLn(array(1, 2) in array(1));
writeLn(1 not in array(0, 2));
```
结果说明:
- `1 in array(1, 2, 2)` 输出 `1`
- `1 in array(0, 2)` 输出 `0`
- `1 in array((1), (2))` 输出 `1`
- `array(1, 2) in array(1, 2, 3, 4)` 输出 `1`
- `array(1, 3) in array((1, 2), (3, 4))` 输出 `1`
- `array(1, 2) in array(1)` 输出 `0`
- `1 not in array(0, 2)` 输出 `1`
代码块身份:输出片段
```text
1
0
1
1
```
`sqlin` 按整行判断左侧是否存在于右侧结果集中:
代码块身份:可直接照写示例
```tsl
writeLn(1 sqlin array(1, 2));
writeLn(array(1, 2) sqlin array(1, 2, 3));
writeLn(array(1, 2) sqlin array((1, 2), (3, 4)));
writeLn(array(5, 6) not sqlin array((1, 2), (3, 4)));
```
结果说明:
- `1 sqlin array(1, 2)` 输出 `1`
- `array(1, 2) sqlin array(1, 2, 3)` 输出 `0`
- `array(1, 2) sqlin array((1, 2), (3, 4))` 输出 `1`
- `array(5, 6) not sqlin array((1, 2), (3, 4))` 输出 `1`
- `in` 看元素或子集
- `sqlin` 看整行
### 行集合并、交、差、对称差
下面这组最小例子展示“按行运算”和“结果会折叠重复行”:
代码块身份:可直接照写示例
```tsl
left_rows := array((1, 2), (1, 2), (2, 3));
right_rows := array((1, 2), (3, 4));
union_rows := left_rows union2 right_rows;
intersect_rows := left_rows intersect right_rows;
minus_rows := left_rows minus right_rows;
outersect_rows := left_rows outersect right_rows;
```
结果说明:
- `left_rows union2 right_rows` 共有三行:`(1,2)``(2,3)``(3,4)`
- `left_rows intersect right_rows` 只有一行:`(1,2)`
- `left_rows minus right_rows` 只有一行:`(2,3)`
- `left_rows outersect right_rows` 有两行:`(2,3)``(3,4)`
- `left_rows` 原本有两行相同的 `(1,2)`,但 `union2` / `intersect` 的结果都只保留一份,说明集合运算会折叠重复行
更大的四列结果集文档结果:
- `array((1,2,3,4),(2,3,4,5),(1,1,1,1)) union2 array((1,2,3,4),(3,4,5,6),(2,2,2,2))`
返回 `array((1,2,3,4),(2,3,4,5),(1,1,1,1),(3,4,5,6),(2,2,2,2))`
- 同一组输入下:
`intersect` 返回 `array((1,2,3,4))`
- 同一组输入下:
`minus` 返回 `array((2,3,4,5),(1,1,1,1))`
- 同一组输入下:
`outersect` 返回 `array((2,3,4,5),(1,1,1,1),(3,4,5,6),(2,2,2,2))`
## 和过滤运算的区别
- 集合运算先把数据当成“行集合”来看,再做包含、并交差。
- 过滤运算先保留“原结果集里的每一条命中记录”;因此重复行会保留下来。
- 需要“集合关系”时留在本页。
- 需要“从原表里筛出哪些行”时看 [13_resultset_and_filters.md](13_resultset_and_filters.md)。
## 默认生成模板
按需求语义从下面两种模板中选择:
代码块身份:可直接照写示例
```tsl
matched := 1 in array(1, 2, 3);
row_matched := array(1, 2) sqlin array((1, 2), (3, 4));
```
### 点前缀比较算符产生逻辑数组
点前缀比较(`.=``.<>``.>``.>=``.<``.<=`)对数组/矩阵逐元素比较,返回真假值数组:
代码块身份:可直接照写示例
```tsl
a := array(0.3, 0.6, 0.9);
result := a .> 0.5;
writeLn("result[0]:", result[0]);
writeLn("result[1]:", result[1]);
writeLn("result[2]:", result[2]);
```
代码块身份:输出片段
```text
result[0]: 0
result[1]: 1
result[2]: 1
```
说明:
- `.>` 对数组的每个元素逐一比较,返回 `0`(假)或 `1`(真)
- `a .> 0.5` 返回 `array(0, 1, 1)`,对应 `0.3 > 0.5` 为假、`0.6 > 0.5` 为真、`0.9 > 0.5` 为真
- 其他点前缀比较同理:`.=`(等于)、`.<>`(不等于)、`.>=``.<``.<=`
配合 `mfind` 转换为下标数组,可用于取子集:
代码块身份:可直接照写示例
```tsl
a := array((0.3, 10), (0.6, 20), (0.9, 30));
logical_array := a[:, 0] .> 0.5;
indexes := mfind(logical_array);
subset := a[indexes];
writeLn("逻辑数组:", tostn(logical_array));
writeLn("下标数组:", tostn(indexes));
writeLn("子集行数:", mrows(subset));
writeLn("子集 (0,0):", subset[0][0]);
writeLn("子集 (0,1):", subset[0][1]);
writeLn("子集 (1,0):", subset[1][0]);
```
代码块身份:输出片段
```text
逻辑数组: array(0,1,1)
下标数组: array(1,2)
子集行数: 2
子集 (0,0): 0.6
子集 (0,1): 20
子集 (1,0): 0.9
```
说明:
- `a[:, 0] .> 0.5` 提取第 0 列并逐元素比较,得到逻辑数组 `array(0, 1, 1)`
- `mfind(逻辑数组)` 把真值位置转换成下标数组 `array(1, 2)`
- `a[indexes]` 按下标提取对应行,等价于 `select * from a where [0] > 0.5 end`
- `mfind` 的完整用法见 [22_matrix_deep_dive.md](22_matrix_deep_dive.md)
### 非完全矩阵与缺位当 0 处理
基础算符作用于非完全矩阵(行长度不一致或字符串键不对齐的数组)时,对应位置不存在或为 `nil` 时**默认当 0 处理**
代码块身份:可直接照写示例
```tsl
a := array("A": 1, "B": 1, "C": 1);
b := array("A": 2, "C": 2);
result := a + b;
writeLn("result['A']:", result["A"]);
writeLn("result['B']:", result["B"]);
writeLn("result['C']:", result["C"]);
```
代码块身份:输出片段
```text
result['A']: 3
result['B']: 1
result['C']: 3
```
说明:
- `a` 有键 `"A"``"B"``"C"``b` 只有 `"A"``"C"`
- `a + b` 时,`b["B"]` 不存在,按 `0` 处理
- 结果 `result["A"] = 1 + 2 = 3``result["B"] = 1 + 0 = 1``result["C"] = 1 + 2 = 3`
标量与矩阵的广播:
代码块身份:可直接照写示例
```tsl
matrix_value := array((1, 2), (3, 4));
result := matrix_value + 10;
writeLn("(0,0):", result[0][0]);
writeLn("(0,1):", result[0][1]);
writeLn("(1,0):", result[1][0]);
writeLn("(1,1):", result[1][1]);
```
代码块身份:输出片段
```text
(0,0): 11
(0,1): 12
(1,0): 13
(1,1): 14
```
说明:
- 标量 `+``-``*``/` 等作用于矩阵时,会广播到每个元素
- `matrix_value + 10` 每个元素都加 10
- 这些是逐元素运算(element-wise),区别于矩阵乘法 `:*`,见 [22_matrix_deep_dive.md](22_matrix_deep_dive.md)
## 本页不生成的范围
- 专门的结果集过滤函数
- TS-SQL 查询与写回
- 更大范围的矩阵函数族
这些内容分别进入 [13_resultset_and_filters.md](13_resultset_and_filters.md)、[14_ts_sql.md](14_ts_sql.md)、[22_matrix_deep_dive.md](22_matrix_deep_dive.md) 或 [23_fmarray.md](23_fmarray.md),不再外跳到原目录。
## 禁止项
- 不要把字符串下标按数组的 `0` 起始规则来写;字符串索引回看 [03_values_and_literals.md](03_values_and_literals.md)。
- 不要把矩阵链式比较 `::...` 和标量链式比较混写成同一种语法。
- 不要把 `in``sqlin` 当成同一个概念。
- 不要期待 `union2` 保留重复行。
- 不要用集合运算去做”保留原始重复记录”的过滤任务。
- 不要把二维结果集默认当成”按元素逐个比较”的集合运算。
- 左侧数组要表达”这些值是否都属于右侧集合”时,用 `in`
- 左侧数组要表达”这一整行是否存在于右侧结果集”时,用 `sqlin`
- `minus` 表达集合差集;如果任务要求保留左侧原始重复次数,改走 [13_resultset_and_filters.md](13_resultset_and_filters.md) 的过滤规则。
- 不要在本页发明结果集过滤、TS-SQL 查询、写回语法或更大矩阵函数族。
- 不要把普通 `array(...)` 自动升级成 `FMArray`;只有任务明确命中时才进入 [23_fmarray.md](23_fmarray.md)。
- 不要把点前缀比较 `.>` 和矩阵链式比较 `::>` 混用;`.>` 返回逻辑数组,`::>` 是链式比较。
- 不要以为非完全矩阵缺位会报错;默认当 `0` 处理。