Files
playbook/skills/tsl-syntax-reference/references/12_matrix_and_collections.md
T

12 KiB
Raw Blame History

TSL 数组、键表、矩阵样数据与集合运算

文档类型:语法主线 是否可直接用于生成代码:是 是否含可直接照写示例:是 是否含不可照写反例:否 遇到不确定时:先按本页候选页继续判断;13_resultset_and_filters.md22_matrix_deep_dive.md23_fmarray.md14_ts_sql.md;仍在语法层但不命中时回到本 Skill 路由中心 index.md;如果问题已经超出语法层,停止并交接给对应事实所有者

这一篇收拢 array(...) 的扩展用法:顺序数组、字符串键表、嵌套数组、本页明确的矩阵样比较,以及 in / sqlin / union2 / intersect / minus / outersect 这类去重型集合关系。

本篇职责

回答“array(...) 在 TSL 里除了最普通的一维数组,还能怎样组织数据;哪些矩阵样写法属于本页文档明确形态;元素、子集、整行和行集合关系应该怎样判断”。

智能体数组/矩阵样数据判断流程

  1. 先判断需要顺序数组、字符串键表、嵌套数组、矩阵样比较,还是去重型集合关系。
  2. 普通容器优先从 array(...) 起手;下标和键访问回看值语法页。
  3. 矩阵链式比较只照 :: 系列文档明确示例写,不要混用标量链式比较。
  4. 基础成员判断用 in / not in;整行存在判断用 sqlin / not sqlin;去重型行集合并、交、差、对称差用 union2 / intersect / minus / outersect
  5. 结果集过滤或 TS-SQL 查询需求分别跳转到 13_resultset_and_filters.md14_ts_sql.md
  6. 没有对应代码块时不要发明数组/矩阵样数据/集合关系写法。

核心规则

  • array(...) 既可以写顺序数组,也可以写字符串键表。
  • 顺序数组下标从 0 开始;字符串仍然从 1 开始。
  • array(...) 可以继续嵌套,形成二维或矩阵样数据。
  • 矩阵链式比较 ::>::<::<>::==::>=::<= 属于本页文档明确形态。
  • in / not in 处理的是元素存在关系,以及左侧为数组时的子集关系。
  • sqlin / not sqlin 处理的是行存在关系;左侧要当成一整行去匹配右侧结果集。
  • union2intersectminusoutersect 都按“行”运算,而不是按单元格逐个运算。
  • 集合运算结果会折叠重复行;如果需求是保留重复记录,改看 13_resultset_and_filters.md
  • 当数据本身就是一维数组时,按行集合运算和按元素集合运算是一致的。
  • 左侧是数组时,先判定需求语义:子集关系用 in,整行存在关系用 sqlin

可直接照写示例

基础数组与键表

顺序数组与字符串键表:

代码块身份:可直接照写示例

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"

嵌套数组:

代码块身份:可直接照写示例

[r1, r2] := array((1, 2), (3, 4));
writeLn(r1[0]);
writeLn(r1[1]);
writeLn(r2[0]);
writeLn(r2[1]);

结果说明:

  • 依次输出 1234
  • 说明 array((1, 2), (3, 4)) 这种嵌套数组写法可以按普通数组继续读取

代码块身份:输出片段

1
2
3
4

矩阵样比较

矩阵链式比较:

代码块身份:可直接照写示例

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) 的三个元素依次输出 101
  • array(1, 2, -1) ::< 2 ::< array(3, 2, 1) 的三个元素依次输出 100
  • 说明矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用

innot insqlinnot sqlin

in 既可以判断单个元素是否存在,也可以判断左侧数组是否是右侧结果集的子集:

代码块身份:可直接照写示例

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

代码块身份:输出片段

1
0
1
1

sqlin 按整行判断左侧是否存在于右侧结果集中:

代码块身份:可直接照写示例

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 看整行

行集合并、交、差、对称差

下面这组最小例子展示“按行运算”和“结果会折叠重复行”:

代码块身份:可直接照写示例

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))

和过滤运算的区别

  • 集合运算先把数据当成“行集合”来看,再做包含、并交差。
  • 过滤运算先保留“原结果集里的每一条命中记录”;因此重复行会保留下来。
  • 需要“集合关系”时留在本页。
  • 需要“从原表里筛出哪些行”时看 13_resultset_and_filters.md

默认生成模板

按需求语义从下面两种模板中选择:

代码块身份:可直接照写示例

matched := 1 in array(1, 2, 3);
row_matched := array(1, 2) sqlin array((1, 2), (3, 4));

点前缀比较算符产生逻辑数组

点前缀比较(.=.<>.>.>=.<.<=)对数组/矩阵逐元素比较,返回真假值数组:

代码块身份:可直接照写示例

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]);

代码块身份:输出片段

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 转换为下标数组,可用于取子集:

代码块身份:可直接照写示例

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]);

代码块身份:输出片段

逻辑数组: 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 的完整用法见 22_matrix_deep_dive.md

非完全矩阵与缺位当 0 处理

基础算符作用于非完全矩阵(行长度不一致或字符串键不对齐的数组)时,对应位置不存在或为 nil默认当 0 处理

代码块身份:可直接照写示例

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"]);

代码块身份:输出片段

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 = 3result["B"] = 1 + 0 = 1result["C"] = 1 + 2 = 3

标量与矩阵的广播:

代码块身份:可直接照写示例

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]);

代码块身份:输出片段

(0,0): 11
(0,1): 12
(1,0): 13
(1,1): 14

说明:

  • 标量 +-*/ 等作用于矩阵时,会广播到每个元素
  • matrix_value + 10 每个元素都加 10
  • 这些是逐元素运算(element-wise),区别于矩阵乘法 :*,见 22_matrix_deep_dive.md

本页不生成的范围

  • 专门的结果集过滤函数
  • TS-SQL 查询与写回
  • 更大范围的矩阵函数族

这些内容分别进入 13_resultset_and_filters.md14_ts_sql.md22_matrix_deep_dive.md23_fmarray.md,不再外跳到原目录。

禁止项

  • 不要把字符串下标按数组的 0 起始规则来写;字符串索引回看 03_values_and_literals.md
  • 不要把矩阵链式比较 ::... 和标量链式比较混写成同一种语法。
  • 不要把 insqlin 当成同一个概念。
  • 不要期待 union2 保留重复行。
  • 不要用集合运算去做”保留原始重复记录”的过滤任务。
  • 不要把二维结果集默认当成”按元素逐个比较”的集合运算。
  • 左侧数组要表达”这些值是否都属于右侧集合”时,用 in
  • 左侧数组要表达”这一整行是否存在于右侧结果集”时,用 sqlin
  • minus 表达集合差集;如果任务要求保留左侧原始重复次数,改走 13_resultset_and_filters.md 的过滤规则。
  • 不要在本页发明结果集过滤、TS-SQL 查询、写回语法或更大矩阵函数族。
  • 不要把普通 array(...) 自动升级成 FMArray;只有任务明确命中时才进入 23_fmarray.md
  • 不要把点前缀比较 .> 和矩阵链式比较 ::> 混用;.> 返回逻辑数组,::> 是链式比较。
  • 不要以为非完全矩阵缺位会报错;默认当 0 处理。