playbook/docs/tsl/syntax/27_fmarray.md

15 KiB
Raw Permalink Blame History

FMArray

文档类型:语法主线 是否可直接用于生成代码:是 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:26_matrix_deep_dive.md13_matrix_and_collections.md28_ts_sql_core.md

手册位置:第 27 篇,共 32 篇。上一篇:26_matrix_deep_dive.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
  • MInitMInitDiagMRand 都可直接生成 FMArray
  • ArrayToFMMatrixToArray 可在 Array / FMArray 间互转。
  • MSizeMRowsMCols 都支持 FMArray;对三维 FMArrayMSize 已验证会返回全部维度长度。
  • FMArray 已验证支持和标量、FMArrayArray 做基础算符运算。
  • 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,但三者的收尾形式并不完全相同。

已验证语法

常量构造与类型判断

代码块身份:已验证可执行示例

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 的三个元素依次是 123
  • f2 的行数是 2、列数是 2
  • f2 四个单元依次是 1234

MInitMInitDiagMRand

代码块身份:已验证可执行示例

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

ArrayToFMMatrixToArray 与单元格类型转换

代码块身份:已验证可执行示例

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 2Array,内容是 (1,2)(3,8)

尺寸与重构

二维和三维尺寸:

代码块身份:已验证可执行示例

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
  • 三个维度依次是 232

reshape 会保持 FMArray 类型:

代码块身份:已验证可执行示例

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
  • 元素依次是 123455

标量运算与基础算符

代码块身份:已验证可执行示例

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 + 1datatype27,结果是 2,3,4
  • f1 + f2datatype27,结果是 3,5,7
  • f1 + array(2, 3, 4)datatype 仍是 27,结果也是 3,5,7

union2 与左值类型

代码块身份:已验证可执行示例

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 的结果 datatype27
  • 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 f1datatype5
  • array(1, 0, 7.2) union2 f1 的长度也是 6,结果是 1,0,7.2,2,4,5

多维转置与维度交换

三维 FMArray 上,反引号转置会把全部维度倒置:

代码块身份:已验证可执行示例

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

代码块身份:已验证可执行示例

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 会按行拼接,不做去重:

代码块身份:已验证可执行示例

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 的结果 datatype27
  • t1 union t2 的行数是 4、列数是 2
  • 拼接后四行依次是 (1,2)(3,4)(5,5)(7,8)

|:| 会按列拼接:

代码块身份:已验证可执行示例

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.

已验证运行结果:

  • | 的结果 datatype27
  • t1 | t2t1 :| 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)
  • 把上面两条里的 | 改成 :|,结果一致

排序

一维排序:

代码块身份:已验证可执行示例

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

二维按字段排序:

代码块身份:已验证可执行示例

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 的支持

代码块身份:已验证可执行示例

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 的返回 datatype5
  • q1 的行数是 4
  • mselect 的返回 datatype17
  • q2 的行数是 4、列数是 2
  • 说明当前解释器下,对 FMArray 做 TS-SQL 查询时,结果不会保留 datatype=27

insert / delete / update

insert

代码块身份:已验证可执行示例

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

代码块身份:已验证可执行示例

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

代码块身份:已验证可执行示例

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 骨架,从这个开始:

代码块身份:已验证可执行示例

f := fmarray[1, 2, 3];

常见误写

  • 在一个 FMArray 常量里混用不同单元格类型。
  • FMArray 当成支持字符串下标的 array
  • 以为对 FMArray 做普通 select 后,结果还是 datatype=27
  • deleteupdate 的结尾形式写成同一种。
  • 以为 union 可以忽略列结构差异。
  • 以为 array union2 fmarray 还会返回 FMArray

代码块身份:反例 / 不可照写

f := fmarray[1, 2.0, 3];

上面这种写法在当前解释器里会编译失败,错误信息包含 fmarray must be same type

代码块身份:反例 / 不可照写

f := fmarray[1, 2, 3];
WriteLn(f["A"]);

上面这种写法在当前解释器里会运行报错,错误信息包含 fmarray index type error

代码块身份:反例 / 不可照写

delete from d where [1] = 4 end;

这类写法不要直接当成当前默认模板。当前解释器下,我实测 delete ... end; 会报 Statement missing terminator;已验证可用的是 delete ...;

代码块身份:反例 / 不可照写

t1 := fmarray[[1, 2], [3, 4]];
t2 := fmarray[[7, 8, 9]];
u := t1 union t2;

上面这种写法会运行报错,错误信息包含 union dim dismatch。当前已验证的 union 只适用于列结构兼容的 FMArray

跳转指引