# TSL FMArray 文档类型:语法深水专题 是否可直接用于生成代码:是 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;[22_matrix_deep_dive.md](22_matrix_deep_dive.md)、[12_matrix_and_collections.md](12_matrix_and_collections.md)、[14_ts_sql.md](14_ts_sql.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md) 这一篇只讲 `FMArray` 文档主干能力:怎样构造 `FMArray`、怎样判断类型、怎样和 `Array` 互转、怎样读取尺寸、做基础运算、做多维转置与维度交换、做矩阵连接、参与 `select/mselect`,以及 `insert/delete/update` 语法边界。 ## 本篇职责 回答“什么时候该用 `FMArray` 而不是普通 `array`,以及文档明确 `FMArray` 写法有哪些”。 ## 智能体 FMArray 判断流程 1. 先判断是否确实需要 `FMArray`,普通数组能解决时先用普通数组。 2. 构造、类型判断、尺寸读取、转置、连接和 TS-SQL 参与只照本页文档明确形态写。 3. `insert`、`delete`、`update` 的收尾形式分别判断,不要互相套用。 4. FMArray 错误边界按本页反例处理,不要凭普通数组经验修写法。 5. 没有对应代码块时不要发明 FMArray 写法。 ## 核心规则 - `fmarray[...]` 可以直接构造 `FMArray` 常量。 - `dataType(v)` 对 `FMArray` 返回 `27`。 - `dataType(v, 1)` 可以读出 `FMArray` 单元格类型;本页文档类型包括 `0` 整型、`1` 浮点、`20` 64 位整型。 - FMArray 相关函数的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md) 和 [../reference/catalog/math.md](../reference/catalog/math.md);本页只保留 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` 可做按行连接且不去重,但列结构必须兼容。 - `|` 和 `:|` 都可做矩阵并右方(按列连接);行数不一致时会用 `0` 补齐,并且两者表现一致。 - 对 `FMArray` 做普通 `select` 不会保留 `FMArray` 类型;`mselect` 也不会返回 `dataType=27`。 - `insert into a array(...)`、`delete from a where ...;`、`update a set ... where ... end;` 都能作用于 `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 ``` ### `mInit`、`mInitDiag`、`mRand` 代码块身份:可直接照写示例 ```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` 与单元格类型转换 代码块身份:可直接照写示例 ```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)` ### 尺寸与重构 二维和三维尺寸: 代码块身份:可直接照写示例 ```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` ### 标量运算与基础算符 代码块身份:可直接照写示例 ```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` 与左值类型 代码块身份:可直接照写示例 ```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` ### 多维转置与维度交换 三维 `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`、`|`、`:|` `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)` - 把上面两条里的 `|` 改成 `:|`,结果一致 ### 排序 一维排序: 代码块身份:可直接照写示例 ```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` 的支持 代码块身份:可直接照写示例 ```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` `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`。