536 lines
15 KiB
Markdown
536 lines
15 KiB
Markdown
# FMArray
|
||
|
||
文档类型:语法主线
|
||
是否可直接用于生成代码:是
|
||
是否含已验证可执行示例:是
|
||
是否含已验证反例:是
|
||
遇到不确定时跳转到:[26_matrix_deep_dive.md](26_matrix_deep_dive.md)、[13_matrix_and_collections.md](13_matrix_and_collections.md)、[28_ts_sql_core.md](28_ts_sql_core.md)
|
||
|
||
手册位置:第 27 篇,共 32 篇。上一篇:[26_matrix_deep_dive.md](26_matrix_deep_dive.md)。下一篇:[28_ts_sql_core.md](28_ts_sql_core.md)。
|
||
|
||
这一篇只讲当前解释器下已经实际跑通的 `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 位整型。
|
||
- `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` 可做按行连接且不去重,但列结构必须兼容。
|
||
- `|` 和 `:|` 都可做按列连接;当前已验证到行数不一致时会用 `0` 补齐,并且两者表现一致。
|
||
- 对 `FMArray` 做普通 `select` 不会保留 `FMArray` 类型;`mselect` 也不会返回 `datatype=27`。
|
||
- 当前实测里,`insert into a array(...)`、`delete from a where ...;`、`update a set ... where ... end;` 都能作用于 `FMArray`,但三者的收尾形式并不完全相同。
|
||
|
||
## 已验证语法
|
||
|
||
### 常量构造与类型判断
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `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`
|
||
|
||
### `MInit`、`MInitDiag`、`MRand`
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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));
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `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` 与单元格类型转换
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `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)`
|
||
|
||
### 尺寸与重构
|
||
|
||
二维和三维尺寸:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `MSize(f3)` 的长度是 `3`
|
||
- 三个维度依次是 `2`、`3`、`2`
|
||
|
||
`reshape` 会保持 `FMArray` 类型:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `datatype(r1)` 仍然是 `27`
|
||
- `r1` 的长度是 `6`
|
||
- 元素依次是 `1`、`2`、`3`、`4`、`5`、`5`
|
||
|
||
### 标量运算与基础算符
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `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` 与左值类型
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `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`
|
||
|
||
### 多维转置与维度交换
|
||
|
||
三维 `FMArray` 上,反引号转置会把全部维度倒置:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 原矩阵尺寸是 `3,2,1`,转置后尺寸是 `1,2,3`
|
||
- 结果 `datatype` 仍是 `27`
|
||
- 转置后的内容是 `[[[1,1,3],[2,0,-8]]]`
|
||
|
||
只交换指定维度时,使用 `mswap`:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `mswap(f3, 0, 1)` 后尺寸是 `2,3,1`
|
||
- 结果 `datatype` 仍是 `27`
|
||
- 结果内容是 `[[[1],[1],[3]],[[2],[0],[-8]]]`
|
||
|
||
### 矩阵连接:`union`、`|`、`:|`
|
||
|
||
`union` 会按行拼接,不做去重:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `union` 的结果 `datatype` 是 `27`
|
||
- `t1 union t2` 的行数是 `4`、列数是 `2`
|
||
- 拼接后四行依次是 `(1,2)`、`(3,4)`、`(5,5)`、`(7,8)`
|
||
|
||
`|` 和 `:|` 会按列拼接:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `|` 的结果 `datatype` 是 `27`
|
||
- `t1 | t2` 和 `t1 :| t2` 的行数都是 `3`、列数都是 `4`
|
||
- 两种写法的结果都依次是 `(1,2,3,4)`、`(3,4,7,8)`、`(5,5,6,9)`
|
||
|
||
当前还额外验证到:行数不一致时,`|` 和 `:|` 在 `FMArray` 上表现一致,缺失行会用 `0` 补齐。
|
||
|
||
- `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)`
|
||
- 把上面两条里的 `|` 改成 `:|`,结果一致
|
||
|
||
### 排序
|
||
|
||
一维排序:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
s1 := fmarray[10, 2, -3, 8];
|
||
sortarray(s1);
|
||
WriteLn(s1[0], ',', s1[1], ',', s1[2], ',', s1[3]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 排序后结果是 `-3,2,8,10`
|
||
|
||
二维按字段排序:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 排序后依次是 `(5,5)`、`(11,12)`、`(31,4)`
|
||
|
||
### TS-SQL 对 `FMArray` 的支持
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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));
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 普通 `select` 的返回 `datatype` 是 `5`
|
||
- `q1` 的行数是 `4`
|
||
- `mselect` 的返回 `datatype` 是 `17`
|
||
- `q2` 的行数是 `4`、列数是 `2`
|
||
- 说明当前解释器下,对 `FMArray` 做 TS-SQL 查询时,结果不会保留 `datatype=27`
|
||
|
||
### `insert` / `delete` / `update`
|
||
|
||
`insert`:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 插入后行数是 `3`
|
||
- 列数仍是 `3`
|
||
- 新插入的第三行是 `9,9,9`
|
||
|
||
`delete`:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 删除后行数是 `2`
|
||
- 保留的两行是 `(1,2)`、`(5,6)`
|
||
|
||
`update`:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
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]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 更新后行数仍是 `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`。
|
||
|
||
## 跳转指引
|
||
|
||
- 回看矩阵基础:见 [26_matrix_deep_dive.md](26_matrix_deep_dive.md)
|
||
- 看 TS-SQL 基础:见 [28_ts_sql_core.md](28_ts_sql_core.md)
|
||
- 看 TS-SQL 进阶:见 [29_ts_sql_advanced.md](29_ts_sql_advanced.md)
|