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
This commit is contained in:
csh
2026-07-29 15:45:47 +08:00
parent 37a3bf4b0c
commit 40a9885eb4
28 changed files with 1048 additions and 773 deletions
@@ -0,0 +1,545 @@
# TSL FMArray
这一篇只讲 `FMArray` 文档主干能力:怎样构造 `FMArray`、怎样判断类型、怎样和 `Array` 互转、怎样读取尺寸、做基础运算、做多维转置与维度交换、做矩阵连接、参与 `select/mselect`,以及 `insert/delete/update` 语法边界。
## 本篇职责
回答“什么时候该用 `FMArray` 而不是普通 `array`,以及文档明确 `FMArray` 写法有哪些”。
## 核心规则
- `fmarray[...]` 可以直接构造 `FMArray` 常量。
- `dataType(v)``FMArray` 返回 `27`
- `dataType(v, 1)` 可以读出 `FMArray` 单元格类型;本页文档类型包括 `0` 整型、`1` 浮点、`20` 64 位整型。
- FMArray 相关函数的参数规格使用 `tsl-api-reference` skill 按名查询;本页只保留 FMArray 行为示例和返回形态边界。
- `ifFmarray(v)` 可直接判断值是否为 `FMArray`
- `mInit``mInitDiag``mRand` 都可直接生成 `FMArray`
- `arrayToFm``matrixToArray` 可在 `Array` / `FMArray` 间互转。
- `msize``mrows``mcols` 都支持 `FMArray`;对三维 `FMArray``msize` 会返回全部维度长度。
- `FMArray` 支持和标量、`FMArray``Array` 做基础算符运算。
- `union2` 支持 `FMArray` / `Array` 混合运算;结果类型跟随左值。
- 对超过二维的 `FMArray`,反引号转置会把全部维度倒置;`mswap` 可只交换指定维度。
- `union` 可做按行连接且不去重,但列结构必须兼容。
- `|``:|` 都可做矩阵并右方(按列连接);在 `FMArray` 上行数不一致时会用 `0` 补齐,并且两者表现一致(普通 `array` 上两者行为不同:`:|` 缺位补 `nil``|` 直接顺接,见 [21_matrix_deep_dive.md](21_matrix_deep_dive.md))。
-`FMArray` 做普通 `select` 不会保留 `FMArray` 类型;`mselect` 也不会返回 `dataType=27`
- `insert into a array(...)``delete from a where ...;``update a set ... where ... end;` 都能作用于 `FMArray`,但三者的收尾形式并不完全相同。
## 可直接照写示例
### 常量构造与类型判断
<!-- tags: 建 FMArray, 高性能数组, 字面量写法 -->
代码块身份:可直接照写示例
```tsl
f1 := fmarray[1, 2, 3];
f2 := fmarray[[1, 2], [3, 4]];
f3 := fmarray[1.0, 2.0, 3.5];
writeLn(dataType(f1));
writeLn(dataType(f1, 1));
writeLn(dataType(f3, 1));
writeLn(ifFmarray(f1));
writeLn(length(f1));
writeLn(f1[0], ',', f1[1], ',', f1[2]);
writeLn(mrows(f2));
writeLn(mcols(f2));
writeLn(f2[0,0], ',', f2[0,1], ',', f2[1,0], ',', f2[1,1]);
```
结果说明:
- `dataType(f1)` 返回 `27`
- `dataType(f1, 1)` 返回 `0`
- `dataType(f3, 1)` 返回 `1`
- `ifFmarray(f1)` 返回 `1`
- `length(f1)` 返回 `3`
- `f1` 的三个元素依次是 `1``2``3`
- `f2` 的行数是 `2`、列数是 `2`
- `f2` 四个单元依次是 `1``2``3``4`
代码块身份:输出片段
```text
27
0
1
1
3
1,2,3
2
2
1,2,3,4
```
### `mInit`、`mInitDiag`、`mRand`
<!-- tags: 初始化 FMArray, 对角阵, 随机矩阵 -->
代码块身份:可直接照写示例
```tsl
fm1 := mInit(5, 3);
fm2 := mInit(array(3, 2), 1L);
fd1 := mInitDiag(3, 3, 1);
fr1 := mRand(2, 3);
writeLn(dataType(fm1, 1));
writeLn(length(fm1));
writeLn(fm1[0], ',', fm1[1], ',', fm1[2], ',', fm1[3], ',', fm1[4]);
writeLn(dataType(fm2, 1));
writeLn(mrows(fm2));
writeLn(mcols(fm2));
writeLn(fd1[0,0], ',', fd1[0,1], ',', fd1[1,0], ',', fd1[1,1], ',', fd1[2,2]);
writeLn(mrows(fr1));
writeLn(mcols(fr1));
```
结果说明:
- `mInit(5, 3)` 生成长度为 `5` 的整型 `FMArray`,五个元素都是 `3`
- `dataType(fm1, 1)` 返回 `0`
- `mInit(array(3, 2), 1L)` 的单元格类型是 `20`
- `fm2` 的行数是 `3`、列数是 `2`
- `mInitDiag(3, 3, 1)``(0,0)``(1,1)``(2,2)``1`,而 `(0,1)``(1,0)``0`
- `mRand(2, 3)` 的行数是 `2`、列数是 `3`
### `arrayToFm`、`matrixToArray` 与单元格类型转换
<!-- tags: 普通数组转 FMArray, 互转, 单元格类型 -->
代码块身份:可直接照写示例
```tsl
a1 := array(1, 2, 3.5);
f1 := arrayToFm(a1, 0);
f2 := arrayToFm(a1, 0.0);
f3 := int64(fmarray[1, 2, 3]);
a2 := matrixToArray(fmarray[[1, 2], [3, 8]]);
writeLn(dataType(f1, 1));
writeLn(f1[0], ',', f1[1], ',', f1[2]);
writeLn(dataType(f2, 1));
writeLn(f2[0], ',', f2[1], ',', f2[2]);
writeLn(dataType(f3, 1));
writeLn(f3[0], ',', f3[1], ',', f3[2]);
writeLn(mrows(a2));
writeLn(mcols(a2));
writeLn(a2[0][0], ',', a2[0][1], ',', a2[1][0], ',', a2[1][1]);
```
结果说明:
- `arrayToFm(a1, 0)` 的单元格类型是 `0`,结果是 `1,2,3`
- `arrayToFm(a1, 0.0)` 的单元格类型是 `1`,结果是 `1,2,3.5`
- `int64(fmarray[1, 2, 3])` 的单元格类型是 `20`
- `matrixToArray(fmarray[[1, 2], [3, 8]])` 返回一个 `2 x 2``Array`,内容是 `(1,2)``(3,8)`
### 尺寸与重构
<!-- tags: FMArray 尺寸, 改形状, reshape, 三维 -->
二维和三维尺寸:
代码块身份:可直接照写示例
```tsl
f3 := fmarray[[[1, 1], [2, 2], [3, 3]], [[2, 2], [3, 3], [4, 4]]];
s := msize(f3);
writeLn(length(s));
writeLn(s[0], ',', s[1], ',', s[2]);
```
结果说明:
- `msize(f3)` 的长度是 `3`
- 三个维度依次是 `2``3``2`
`reshape` 会保持 `FMArray` 类型:
代码块身份:可直接照写示例
```tsl
f1 := fmarray[[1, 2], [3, 4], [5, 5]];
r1 := reshape(f1, 6);
writeLn(dataType(r1));
writeLn(length(r1));
writeLn(r1[0], ',', r1[1], ',', r1[2], ',', r1[3], ',', r1[4], ',', r1[5]);
```
结果说明:
- `dataType(r1)` 仍然是 `27`
- `r1` 的长度是 `6`
- 元素依次是 `1``2``3``4``5``5`
### 标量运算与基础算符
<!-- tags: FMArray 加减乘除, 和数字运算 -->
代码块身份:可直接照写示例
```tsl
f1 := fmarray[1, 2, 3];
f2 := fmarray[2, 3, 4];
a1 := array(2, 3, 4);
s0 := f1 + 1;
s1 := f1 + f2;
s2 := f1 + a1;
writeLn(dataType(s0));
writeLn(s0[0], ',', s0[1], ',', s0[2]);
writeLn(dataType(s1));
writeLn(s1[0], ',', s1[1], ',', s1[2]);
writeLn(dataType(s2));
writeLn(s2[0], ',', s2[1], ',', s2[2]);
```
结果说明:
- `f1 + 1``dataType``27`,结果是 `2,3,4`
- `f1 + f2``dataType``27`,结果是 `3,5,7`
- `f1 + array(2, 3, 4)``dataType` 仍是 `27`,结果也是 `3,5,7`
### `union2` 与左值类型
<!-- tags: FMArray 合并, 左值类型决定结果 -->
代码块身份:可直接照写示例
```tsl
f1 := fmarray[1, 2, 0, 4, 5];
a1 := array(1, 0, 7.2);
u1 := f1 union2 a1;
u2 := a1 union2 f1;
writeLn(dataType(u1));
writeLn(dataType(u1, 1));
writeLn(length(u1));
writeLn(u1[0], ',', u1[1], ',', u1[2], ',', u1[3], ',', u1[4], ',', u1[5]);
writeLn(dataType(u2));
writeLn(length(u2));
writeLn(u2[0], ',', u2[1], ',', u2[2], ',', u2[3], ',', u2[4], ',', u2[5]);
```
结果说明:
- `union2` 的结果 `dataType``27`
- `f1 union2 array(1, 0, 7.2)` 的单元格类型会提升为 `1` 浮点型
- `f1 union2 array(1, 0, 7.2)` 的长度是 `6`,结果是 `1,2,0,4,5,7.2`
- `array(1, 0, 7.2) union2 f1``dataType``5`
- `array(1, 0, 7.2) union2 f1` 的长度也是 `6`,结果是 `1,0,7.2,2,4,5`
### 多维转置与维度交换
<!-- tags: 三维转置, 换维度, 维度倒置 -->
三维 `FMArray` 上,反引号转置会把全部维度倒置:
代码块身份:可直接照写示例
```tsl
f3 := fmarray[[[1], [2]], [[1], [0]], [[3], [-8]]];
t := `f3;
s := msize(t);
writeLn(dataType(t));
writeLn(length(s));
writeLn(s[0], ',', s[1], ',', s[2]);
writeLn(t[0,0,0], ',', t[0,0,1], ',', t[0,0,2], ';', t[0,1,0], ',', t[0,1,1], ',', t[0,1,2]);
```
结果说明:
- 原矩阵尺寸是 `3,2,1`,转置后尺寸是 `1,2,3`
- 结果 `dataType` 仍是 `27`
- 转置后的内容是 `[[[1,1,3],[2,0,-8]]]`
只交换指定维度时,使用 `mswap`
代码块身份:可直接照写示例
```tsl
f3 := fmarray[[[1], [2]], [[1], [0]], [[3], [-8]]];
t := mswap(f3, 0, 1);
s := msize(t);
writeLn(dataType(t));
writeLn(length(s));
writeLn(s[0], ',', s[1], ',', s[2]);
writeLn(t[0,0,0], ',', t[0,1,0], ',', t[0,2,0], ';', t[1,0,0], ',', t[1,1,0], ',', t[1,2,0]);
```
结果说明:
- `mswap(f3, 0, 1)` 后尺寸是 `2,3,1`
- 结果 `dataType` 仍是 `27`
- 结果内容是 `[[[1],[1],[3]],[[2],[0],[-8]]]`
### 矩阵连接 / 矩阵并右方:`union`、`|`、`:|`
<!-- tags: FMArray 拼接, 按列连接, 补零对齐 -->
`union` 会按行拼接,不做去重:
代码块身份:可直接照写示例
```tsl
t1 := fmarray[[1, 2], [3, 4], [5, 5]];
t2 := fmarray[[7, 8]];
u := t1 union t2;
writeLn(dataType(u));
writeLn(mrows(u));
writeLn(mcols(u));
writeLn(u[0,0], ',', u[0,1], ';', u[1,0], ',', u[1,1], ';', u[2,0], ',', u[2,1], ';', u[3,0], ',', u[3,1]);
```
结果说明:
- `union` 的结果 `dataType``27`
- `t1 union t2` 的行数是 `4`、列数是 `2`
- 拼接后四行依次是 `(1,2)``(3,4)``(5,5)``(7,8)`
`|``:|` 会执行矩阵并右方,也就是按列拼接:
代码块身份:可直接照写示例
```tsl
t1 := fmarray[[1, 2], [3, 4], [5, 5]];
t2 := fmarray[[3, 4], [7, 8], [6, 9]];
u1 := t1 | t2;
u2 := t1 :| t2;
writeLn(dataType(u1));
writeLn(mrows(u1));
writeLn(mcols(u1));
writeLn(u1[0,0], ',', u1[0,1], ',', u1[0,2], ',', u1[0,3]);
writeLn(u1[1,0], ',', u1[1,1], ',', u1[1,2], ',', u1[1,3]);
writeLn(u1[2,0], ',', u1[2,1], ',', u1[2,2], ',', u1[2,3]);
writeLn(u2[0,0], ',', u2[0,1], ',', u2[0,2], ',', u2[0,3]);
writeLn(u2[1,0], ',', u2[1,1], ',', u2[1,2], ',', u2[1,3]);
writeLn(u2[2,0], ',', u2[2,1], ',', u2[2,2], ',', u2[2,3]);
```
代码块身份:输出片段
```text
27
3
4
1,2,3,4
3,4,7,8
5,5,6,9
1,2,3,4
3,4,7,8
5,5,6,9
```
结果说明:
- `|` 的结果 `dataType``27`
- `t1 | t2``t1 :| t2` 的行数都是 `3`、列数都是 `4`
- 两种写法的结果都依次是 `(1,2,3,4)``(3,4,7,8)``(5,5,6,9)`
行数不一致时,`|``:|``FMArray` 上表现一致,缺失行会用 `0` 补齐。
代码块身份:可直接照写示例
```tsl
m1 := fmarray[[1, 2], [3, 4], [5, 5]] | fmarray[[3, 4]];
m2 := fmarray[[3, 4]] | fmarray[[1, 2], [3, 4], [5, 5]];
writeLn(m1[0,0], ',', m1[0,1], ',', m1[0,2], ',', m1[0,3]);
writeLn(m1[1,0], ',', m1[1,1], ',', m1[1,2], ',', m1[1,3]);
writeLn(m1[2,0], ',', m1[2,1], ',', m1[2,2], ',', m1[2,3]);
writeLn(m2[0,0], ',', m2[0,1], ',', m2[0,2], ',', m2[0,3]);
writeLn(m2[1,0], ',', m2[1,1], ',', m2[1,2], ',', m2[1,3]);
writeLn(m2[2,0], ',', m2[2,1], ',', m2[2,2], ',', m2[2,3]);
```
代码块身份:输出片段
```text
1,2,3,4
3,4,0,0
5,5,0,0
3,4,1,2
0,0,3,4
0,0,5,5
```
- `fmarray[[1, 2], [3, 4], [5, 5]] | fmarray[[3, 4]]` 的结果三行依次是 `(1,2,3,4)``(3,4,0,0)``(5,5,0,0)`
- `fmarray[[3, 4]] | fmarray[[1, 2], [3, 4], [5, 5]]` 的结果三行依次是 `(3,4,1,2)``(0,0,3,4)``(0,0,5,5)`
- 把上面两条里的 `|` 改成 `:|`,结果一致
### 排序
<!-- tags: 排序, 从小到大, 按列排, sortArray -->
一维排序:
代码块身份:可直接照写示例
```tsl
s1 := fmarray[10, 2, -3, 8];
sortArray(s1);
writeLn(s1[0], ',', s1[1], ',', s1[2], ',', s1[3]);
```
结果说明:
- 排序后结果是 `-3,2,8,10`
二维按字段排序:
代码块身份:可直接照写示例
```tsl
f1 := fmarray[[11, 12], [31, 4], [5, 5]];
sortTableByField(f1, 0, 1);
writeLn(f1[0,0], ',', f1[0,1], ';', f1[1,0], ',', f1[1,1], ';', f1[2,0], ',', f1[2,1]);
```
结果说明:
- 排序后依次是 `(5,5)``(11,12)``(31,4)`
### TS-SQL 对 `FMArray` 的支持
<!-- tags: 对 FMArray 做查询, mselect -->
代码块身份:可直接照写示例
```tsl
q1 := select * from fmarray[[1, 2], [0, 4], [1, 2], [5, 6]] end;
q2 := mselect * from fmarray[[1, 2], [0, 4], [1, 2], [5, 6]] end;
writeLn(dataType(q1));
writeLn(mrows(q1));
writeLn(dataType(q2));
writeLn(mrows(q2));
writeLn(mcols(q2));
```
结果说明:
- 普通 `select` 的返回 `dataType``5`
- `q1` 的行数是 `4`
- `mselect` 的返回 `dataType``17`
- `q2` 的行数是 `4`、列数是 `2`
- 说明对 `FMArray` 做 TS-SQL 查询时,结果不会保留 `dataType=27`
### `insert` / `delete` / `update`
<!-- tags: FMArray 增删改, 写回 -->
`insert`
代码块身份:可直接照写示例
```tsl
a := mInit(2, 3, 1.0);
insert into a array(9);
writeLn(mrows(a));
writeLn(mcols(a));
writeLn(a[2,0], ',', a[2,1], ',', a[2,2]);
```
结果说明:
- 插入后行数是 `3`
- 列数仍是 `3`
- 新插入的第三行是 `9,9,9`
`delete`
代码块身份:可直接照写示例
```tsl
d := fmarray[[1, 2], [0, 4], [5, 6]];
delete from d where [1] = 4;
writeLn(mrows(d));
writeLn(d[0,0], ',', d[0,1], ';', d[1,0], ',', d[1,1]);
```
结果说明:
- 删除后行数是 `2`
- 保留的两行是 `(1,2)``(5,6)`
`update`
代码块身份:可直接照写示例
```tsl
u := fmarray[[1, 2], [0, 4], [5, 6]];
update u set [0] = 100 where [1] = 4 end;
writeLn(mrows(u));
writeLn(u[0,0], ',', u[0,1], ';', u[1,0], ',', u[1,1], ';', u[2,0], ',', u[2,1]);
```
结果说明:
- 更新后行数仍是 `3`
- 结果三行依次是 `(1,2)``(100,4)``(5,6)`
这三种写法的收尾形式为:
- `insert into a array(9);`
- `delete from d where [1] = 4;`
- `update u set [0] = 100 where [1] = 4 end;`
## 本页不生成的范围
- `mInitDiag` 更高维行为
- `mRand` 的随机分布参数变体
- `union` / `|` / `:|` 与普通 `array` 的更多混合边界
- `left join` / `right join` / 更复杂 SQL 写回
- CopyOnWrite 的内存级行为
这些不作为本页可生成事实。
## 默认生成模板
需要 `FMArray` 常量时,优先从这个最短模板开始:
代码块身份:可直接照写示例
```tsl
f := fmarray[1, 2, 3];
```
## 禁止项
- 在一个 `FMArray` 常量里混用不同单元格类型。
-`FMArray` 当成支持字符串下标的 `array`
- 以为对 `FMArray` 做普通 `select` 后,结果还是 `dataType=27`
-`delete``update` 的结尾形式写成同一种。
- 以为 `union` 可以忽略列结构差异。
- 以为 `array union2 fmarray` 还会返回 `FMArray`
代码块身份:反例 / 不可照写
```text
f := fmarray[1, 2.0, 3];
```
上面这种写法不作为可写事实,会编译失败,错误信息包含 `fmarray must be same type`
代码块身份:反例 / 不可照写
```text
f := fmarray[1, 2, 3];
writeLn(f["A"]);
```
上面这种写法不作为可写事实,会运行报错,错误信息包含 `fmarray index type error`
代码块身份:反例 / 不可照写
```text
delete from d where [1] = 4 end;
```
这类写法不要直接当成默认模板。`delete ... end;` 会报 `Statement missing terminator`;文档明确写法是 `delete ...;`
代码块身份:反例 / 不可照写
```text
t1 := fmarray[[1, 2], [3, 4]];
t2 := fmarray[[7, 8, 9]];
u := t1 union t2;
```
上面这种写法会运行报错,错误信息包含 `union dim dismatch`。本页明确的 `union` 只适用于列结构兼容的 `FMArray`