Files
csh 40a9885eb4 feat(tsl-syntax-reference): harden retrieval and restructure pages
- flag weak candidates (no intent/heading/identifier/tag hit) and exit 2
  when every candidate is weak: mis-hits used to be indistinguishable
  from real hits, so the retry-with-better-terms loop never fired
- accept multiple ids per --section for batch fetch, failing atomically
  on any unknown id so a partial fetch cannot pass as complete
- move query synonyms and page intent aliases to data/lexicon.json and
  enforce alias/page correspondence in --check; curation data no longer
  lives in the engine
- document the weak-hit rule, batch fetch and prelude-once guidance in
  SKILL.md, with curation discipline in data/README.md
- drop 11_pitfalls.md, renumber the trailing pages and spread retrieval
  tags across topics; lexicon keys are page filenames, so the renumbering
  and the new --check rule cannot land in separate commits
2026-07-29 15:45:47 +08:00

360 lines
11 KiB
Markdown
Raw Permalink 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 里除了最普通的一维数组,还能怎样组织数据;哪些矩阵样写法属于本页文档明确形态;元素、子集、整行和行集合关系应该怎样判断”。
## 核心规则
- `array(...)` 既可以写顺序数组,也可以写字符串键表。
- 顺序数组下标从 `0` 开始;字符串仍然从 `1` 开始。
- `array(...)` 可以继续嵌套,形成二维或矩阵样数据。
- 矩阵链式比较 `::>``::<``::<>``::==``::>=``::<=` 属于本页文档明确形态。
- `in` / `not in` 处理的是元素存在关系,以及左侧为数组时的子集关系。
- `sqlin` / `not sqlin` 处理的是行存在关系;左侧要当成一整行去匹配右侧结果集。
- `union2``intersect``minus``outersect` 都按“行”运算,而不是按单元格逐个运算。
- 集合运算结果会折叠重复行;保留重复记录的过滤规则见 [12_resultset_and_filters.md](12_resultset_and_filters.md)。
- 当数据本身就是一维数组时,按行集合运算和按元素集合运算是一致的。
- 左侧是数组时,先判定需求语义:子集关系用 `in`,整行存在关系用 `sqlin`
## 可直接照写示例
### 基础数组与键表
<!-- tags: 建数组, 字典, 键值对, 二维数组, 嵌套数组, 按名字取值 -->
顺序数组与字符串键表:
代码块身份:可直接照写示例
```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
```
### 矩阵样比较
<!-- tags: 数组比大小, 链式比较, 整体比较 -->
矩阵链式比较:
代码块身份:可直接照写示例
```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`
<!-- tags: 判断存在, 是否包含, 在不在里面, 子集判断 -->
`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
1
0
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` 看整行
### 行集合并、交、差、对称差
<!-- tags: 交集, 并集, 差集, 合并两个数组, 去重合并, 共同部分 -->
下面这组最小例子展示“按行运算”和“结果会折叠重复行”:
代码块身份:可直接照写示例
```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))`
## 和过滤运算的区别
<!-- tags: 集合还是过滤, 重复行怎么办, 两种运算怎么选 -->
- 集合运算先把数据当成“行集合”来看,再做包含、并交差。
- 过滤运算先保留“原结果集里的每一条命中记录”;因此重复行会保留下来。
- 需要“集合关系”时留在本页。
- “从原表里筛出哪些行”的过滤规则见 [12_resultset_and_filters.md](12_resultset_and_filters.md)。
## 默认生成模板
按需求语义从下面两种模板中选择:
代码块身份:可直接照写示例
```tsl
matched := 1 in array(1, 2, 3);
row_matched := array(1, 2) sqlin array((1, 2), (3, 4));
```
## 点前缀比较算符产生逻辑数组
<!-- tags: 逐元素比较, 得到真假数组, 批量判断, 掩码 -->
点前缀比较(`.=``.<>``.>``.>=``.<``.<=`)对数组/矩阵逐元素比较,返回真假值数组:
代码块身份:可直接照写示例
```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` 的完整用法见 [21_matrix_deep_dive.md](21_matrix_deep_dive.md)
## 非完全矩阵缺位当 0 处理与标量广播
<!-- tags: 长度不一致, 缺位补零, 标量广播, 数组和数字运算 -->
基础算符作用于非完全矩阵(行长度不一致或字符串键不对齐的数组)时,对应位置不存在或为 `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),区别于矩阵乘法 `:*`,见 [21_matrix_deep_dive.md](21_matrix_deep_dive.md)
## 本页不生成的范围
- 专门的结果集过滤函数
- TS-SQL 查询与写回
- 更大范围的矩阵函数族
这些名称只作为边界提示,不作为本页可生成模板。
## 禁止项
- 不要把字符串下标按数组的 `0` 起始规则来写;字符串下标从 `1` 开始,详见 [03_values_and_literals.md](03_values_and_literals.md)。
- 不要把矩阵链式比较 `::...` 和标量链式比较混写成同一种语法。
- 不要把 `in``sqlin` 当成同一个概念。
- 不要期待 `union2` 保留重复行。
- 不要用集合运算去做“保留原始重复记录”的过滤任务。
- 不要把二维结果集默认当成“按元素逐个比较”的集合运算。
- 左侧数组要表达“这些值是否都属于右侧集合”时,用 `in`
- 左侧数组要表达“这一整行是否存在于右侧结果集”时,用 `sqlin`
- `minus` 表达集合差集;保留左侧原始重复次数的过滤规则见 [12_resultset_and_filters.md](12_resultset_and_filters.md)。
- 不要在本页发明结果集过滤、TS-SQL 查询、写回语法或更大矩阵函数族。
- 不要把普通 `array(...)` 自动升级成 `FMArray``FMArray` 专属事实见 [22_fmarray.md](22_fmarray.md)。
- 不要把点前缀比较 `.>` 和矩阵链式比较 `::>` 混用;`.>` 返回逻辑数组,`::>` 是链式比较。
- 不要以为非完全矩阵缺位会报错;默认当 `0` 处理。