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

文档类型：语法主线
是否可直接用于生成代码：是
是否含可直接照写示例：是
是否含不可照写反例：否
遇到不确定时：先按本页候选页继续判断；[13_resultset_and_filters.md](13_resultset_and_filters.md)、[22_matrix_deep_dive.md](22_matrix_deep_dive.md)、[23_fmarray.md](23_fmarray.md)、[14_ts_sql.md](14_ts_sql.md)；仍不命中时回到语法路由中心 [index.md](index.md)；如果问题已经超出语法层，回到 TSL 总入口 [../index.md](../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.md](13_resultset_and_filters.md) 或 [14_ts_sql.md](14_ts_sql.md)。
6. 没有对应代码块时不要发明数组/矩阵样数据/集合关系写法。

## 核心规则

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

## 可直接照写示例

### 基础数组与键表

顺序数组与字符串键表：

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

```tsl
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"`

嵌套数组：

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

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

结果说明：

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

代码块身份：输出片段

```text
1
2
3
4
```

### 矩阵样比较

矩阵链式比较：

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

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

### `in`、`not in`、`sqlin`、`not sqlin`

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

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

```tsl
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`

代码块身份：输出片段

```text
1
0
1
1
```

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

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

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

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

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

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

```tsl
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](13_resultset_and_filters.md)。

## 默认生成模板

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

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

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

## 本页不生成的范围

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

这些内容分别进入 [13_resultset_and_filters.md](13_resultset_and_filters.md)、[14_ts_sql.md](14_ts_sql.md)、[22_matrix_deep_dive.md](22_matrix_deep_dive.md) 或 [23_fmarray.md](23_fmarray.md)，不再外跳到原目录。

## 禁止项

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