# TSL 对象重载与迭代 文档类型:语法深水专题 是否可直接用于生成代码:仅部分 是否含可直接照写示例:是 是否含不可照写反例:否 遇到不确定时:先按本页候选页继续判断;[08_objects_and_classes.md](08_objects_and_classes.md)、[20_object_runtime_and_introspection.md](20_object_runtime_and_introspection.md)、[17_types_and_conversions.md](17_types_and_conversions.md);仍在语法层但不命中时回到本 Skill 路由中心 [index.md](index.md);如果问题已经超出语法层,停止并交接给对应事实所有者 这一篇只讲对象直接参与语言级操作的文档能力:基础算符重载、`[]` 重载、`for in` 重载,以及 `mrows` / `mcols` / `msize` 这类矩阵关键字重载。 ## 本篇职责 回答“当类不只是普通对象,而要直接参与 `obj + x`、`obj[index]`、`for v in obj`、`mrows(obj)` 这类语言级操作时,支持哪些文档明确写法”。 ## 智能体对象重载/迭代判断流程 1. 先判断要重载二元算符、下标、`for in`,还是矩阵尺寸函数。 2. 对象重载只照本页明确的 `operator` 签名写,不要从未写入文档资料扩展未知重载。 3. 普通对象模型先回看 `08_objects_and_classes.md`,不要在重载页发明类基础语法。 4. 未列入本页主干的重载族不要写成语法事实。 5. 没有对应代码块时不要发明对象重载/迭代写法。 ## 核心规则 - 对象二元算符重载的最小可靠形态是成员方法 `function operator + (other);` 这一类写法。 - 比较算符可写成 `function operator < (other, is_left);`,用 `is_left` 区分对象在左边还是右边。 - 对象 `[]` 读取有两种文档明确写法:`function operator[](index);` 和 `function operator[0](index, s1);`。 - 对象 `[]` 写入的文档明确写法是 `function operator[1](index, v);`。 - `function operator for(flag);` 可以重载 `for in`。 - 在 `operator for(flag)` 里,`flag .& 2` 可用来区分“一个循环变量”还是“两个循环变量”,`flag .& 1` 可用来区分“第一次进入”还是“继续迭代”。 - `mrows` / `mcols` / `msize` 可以在类里先声明 `function operator mrows(n);` 这类签名,再在类外实现 `function operator ClassName.mrows(n);`。 - 可用形态包括 `mrows(obj)`、`mcols(obj)`、`msize(obj)` 这类关键字调用,以及 `obj.mcols(1)` 这类对象方法式调用。 - `function operator++(v);` 和 `function operator += (v);` 也可用。 - 不要把未写入文档资料里的裸 `function operator;` / `function operator1;` 直接当成语法事实。`::` / `:.` / `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex` 的重载本页已给出可照写形态,照本页示例写即可。 ## 可直接照写示例 ### 二元算符重载 代码块身份:可直接照写示例 ```tsl c1 := new Complex(); c1.RealPart := 10; c1.ImaginaryPart := 100; c2 := c1 + 10; writeLn(c2.RealPart); writeLn(c1 < 5); writeLn(c1 < 300); writeLn(5 < c1); type Complex = class public property RealPart read real_part_ write real_part_; property ImaginaryPart read imaginary_part_ write imaginary_part_; function operator + (other); begin sum := new Complex(); if ifNumber(other) then begin sum.RealPart := real_part_ + other; end else begin sum.RealPart := real_part_ + other.RealPart; sum.ImaginaryPart := imaginary_part_ + other.ImaginaryPart; end return sum; end; function operator < (other, is_left); begin if ifNumber(other) then begin less := real_part_ < other; end else begin less := (real_part_ ^ 2 + imaginary_part_ ^ 2) < other.RealPart ^ 2 + other.ImaginaryPart ^ 2; end if not is_left then less := not less; return less; end; private real_part_; imaginary_part_; end; ``` 结果说明: - 依次输出 `20`、`0`、`1`、`1` - 说明 `obj + value` 可以通过成员 `operator +` 接管 - 说明带 `is_left` 的比较算符可以同时处理 `obj < value` 和 `value < obj` - 私有成员用尾随下划线的 `real_part_` / `imaginary_part_`,对外用 `PascalCase` property 暴露 代码块身份:输出片段 ```text 20 0 1 1 ``` ### `[]` 重载:`operator[]` / `operator[1]` 代码块身份:可直接照写示例 ```tsl t := array(1, 2, 3, 4, 5); b := new IndexableBox(t); writeLn(b[2]); b[3] := 999; writeLn(b.data[3]); type IndexableBox = class public data; function create(v); begin data := v; end; function operator[](index); begin return data[index]; end; function operator[1](index, v); begin data[index] := v; end; end; ``` 结果说明: - 依次输出 `3`、`999` - 说明 `operator[]` 和 `operator[1]` 可以完成单层下标读取和写入 ### `[]` 重载:`operator[0]` / `operator[1]` 沿用上一段的 `bb` 类与测试主体,只把读取签名从 `function operator[](index);` 改成 `function operator[0](index, s1);`: 代码块身份:配置片段 / 概念骨架 ```tsl type IndexableBox = class public // 其余字段、create()、operator[1] 和测试主体同上一段 function operator[0](index, s1); begin return data[index]; end; end; ``` 结果说明: - 依次输出 `3`、`999` - 说明 `operator[0]` / `operator[1]` 这组写法在单层下标场景同样可用 ### `for in` 重载 代码块身份:可直接照写示例 ```tsl box := new Box(array("A", "B", "C")); for v in box do writeLn(v); for i, v in box do writeLn(i$":"$v); type Box = class public data; findex; function create(v); begin data := v; end; function operator for(flag); begin use_pair := flag .& 2; again := flag .& 1; if not again then begin findex := 0; end else if findex < length(data) - 1 then begin findex++; end else begin return nil; end if use_pair then return array(findex, data[findex]); return data[findex]; end; end; ``` 结果说明: - 单变量循环依次输出 `A`、`B`、`C` - 双变量循环依次输出 `0:A`、`1:B`、`2:C` - 说明 `operator for(flag)` 可以重载 `for in` - 也说明同一个对象可以按返回值形态同时支持“单变量遍历”和“索引 + 值遍历” ### `mrows` / `mcols` / `msize` 重载 代码块身份:可直接照写示例 ```tsl g := new GridWrap(); writeLn(mrows(g)); writeLn(mcols(g)); sz := msize(g); writeLn(sz[0]); writeLn(sz[1]); cols := g.mcols(1); writeLn(cols[0]); writeLn(cols[1]); writeLn(cols[2]); type GridWrap = class public data; function create(); begin data := array( ("A": 1, "B": 2, "C": 3), ("A": 4, "B": 5, "C": 6) ); end; function operator mrows(n); function operator mcols(n); function operator msize(n); end; function operator GridWrap.mrows(n); begin _n := ifNil(n) ? 0 : n; return mrows(data, _n); end; function operator GridWrap.mcols(n); begin _n := ifNil(n) ? 0 : n; return mcols(data, _n); end; function operator GridWrap.msize(n); begin _n := ifNil(n) ? 0 : n; return msize(data, _n); end; ``` 结果说明: - 依次输出 `2`、`3`、`2`、`3`、`A`、`B`、`C` - 说明 `mrows(obj)`、`mcols(obj)`、`msize(obj)` 都可由对象重载接管 - 说明对象方法式调用 `obj.mcols(1)` 也可继续取得列下标列表 ### `++` 与 `+=` 代码块身份:可直接照写示例 ```tsl counter := new Counter(10); ++counter; writeLn(counter.data); c := counter++; writeLn(c.data); writeLn(counter.data); counter += 5; writeLn(counter.data); type Counter = class public data; function create(v); begin data := v; end; function operator++(v); begin if v = 0 then begin r := new Counter(); r.data := data; r.data++; return r; end else data++; end; function operator += (v); begin data += v; end; end; ``` 结果说明: - 依次输出 `11`、`11`、`12`、`17` - 说明前置 `++` 会直接修改对象状态 - 说明这个最小样例里,后置 `counter++` 返回的是递增前快照 - 说明 `operator += (v)` 可以接管 `counter += 5` `--` 与 `-=` 与之对称: 代码块身份:可直接照写示例 ```tsl counter := new Counter(10); counter -= 3; writeLn(counter.data); type Counter = class public data; function create(v); begin data := v; end; function operator--(v); begin if v = 0 then begin r := new Counter(); r.data := data; r.data--; return r; end else data--; end; function operator -= (v); begin data -= v; end; end; ``` 结果说明: - 输出 `7` - 说明 `operator -= (v)` 可以接管 `counter -= 3` - `operator--(v)` 与 `operator++(v)` 结构对称:`v = 0` 分支返回递减前快照,否则原地递减 ### 二进制函数重载:`operator funcName` 除了符号算符,`operator` 还能重载具名的全局二进制函数(如 `DateToStr`、`TryStrToInt` 等)。定义写成 `[class] function operator funcName(...)`:`class` 关键字可选,加上表示类方法,不加表示成员函数。 成员函数重载(参数比原函数少 1 个,用第一个参数的对象实例调用): 代码块身份:可直接照写示例 ```tsl d := new IntDate(20240329); writeLn(DateToStr(d)); type IntDate = class public value; function create(v); begin value := v; end; function operator DateToStr(); begin v := IntToDate(value); return DateToStr(v); end; end; ``` 结果说明: - 输出 `2024-03-29` - 说明 `DateToStr(d)` 被对象的成员 `operator DateToStr` 接管 - 成员函数重载时参数个数比原二进制函数少 1 个,第一个实参(对象本身)用于定位方法 类方法重载(`class function`,参数与原函数一致): 代码块身份:可直接照写示例 ```tsl d := new IntDate2(20240329); writeLn(DateToStr(d)); type IntDate2 = class public value; function create(v); begin value := v; end; class function operator DateToStr(t); begin t := ifObj(t) ? t.value : t; return DateToStr(IntToDate(t)); end; end; ``` 结果说明: - 输出 `2024-03-29` - 说明 `class function operator DateToStr(t)` 作为类方法接管调用,参数个数与原二进制函数一致 类内用 `::` 调同名全局函数(避免重载递归): 代码块身份:可直接照写示例 ```tsl c := new ClassA(); c.value := "314"; ret := TryStrToInt(c, msg); writeLn(tostn(array(ret, msg))); type ClassA = class public value; function operator TryStrToInt(msg); begin return ::TryStrToInt(value, msg); end; end; ``` 结果说明: - 输出 `array(1,314)`:`ret` 为 `1`(转换成功),出参 `msg` 为 `314` - 说明类内需要调用被重载的同名全局函数时,用 `::` 前缀指定全局版本,否则会递归回自己 - 重载函数支持通过参数传出返回值(`msg` 作为出参被赋值) ### `::` / `:.` 遍历重载与 `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex` 重载 `::`(二维遍历)或 `:.`(深度遍历)后,对象就能像矩阵一样被 `obj::begin ... end` 遍历。遍历体里用到的 `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex(n)` 也各自重载,返回当前单元的值、行下标、列下标、维度数和第 `n` 维下标。`operator ::(flag)` 的 `flag` 为 `0` 表示第一次循环、`1` 表示后续循环,返回 `0` 或 `nil` 结束遍历、返回非零数字继续: 代码块身份:可直接照写示例 ```tsl t := array("A": 0 -> 3, "B": 10 -> 2, "C": 20 -> 21); traversable := new TraversableMatrix(t); traversable::begin echo "mcell:", mcell, " mrow:", mrow, " mcol:", mcol, " mIndexCount:", mIndexCount, "\r\n"; end type TraversableMatrix = class public data; Rdata; findex; lengtD; function create(v); begin data := v; end; function operator ::(flag); begin if not flag then begin Rdata := array(); k := 0; data::begin Rdata[k] := array(mcell, mIndexCount, mrow, mcol); if mIndexCount > 2 then for i := 2 to mIndexCount - 1 do Rdata[k, i + 2] := mIndex(i); k++; end lengtD := length(Rdata); findex := 0; end else if findex < lengtD - 1 then findex++; else return nil; return 1; end; function operator mcell(); begin return Rdata[findex][0]; end; function operator mIndexCount(); begin return Rdata[findex][1]; end; function operator mrow(); begin return Rdata[findex][2]; end; function operator mcol(); begin return Rdata[findex][3]; end; function operator mIndex(n); begin if n < Rdata[findex][1] then return Rdata[findex][n + 2]; else raise "指定的维度超出最大维度数"; end; end; ``` 结果说明: - 逐行输出每个单元的 `mcell` / `mrow` / `mcol` / `mIndexCount`,遍历顺序与被代理的 `data` 一致 - `operator ::(flag)` 里 `flag=0` 时初始化把 `data` 的遍历结果缓存进 `Rdata`,之后每次推进 `findex` - 返回 `1` 表示继续、返回 `nil` 表示结束 - `:.`(深度遍历)重载方式与 `::` 相同,把内部 `data::begin ... end` 换成 `data:.begin ... end` 即可 - 遍历体里用到的 `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex` 必须各自重载,否则报 `override function not found` ### 关键字函数重载:`msize` / `mrows` / `mcols` `msize` / `mrows` / `mcols` 这类关键字函数也能重载,形态同二进制函数重载 `[class] function operator KeyWord(...)`,但**关键字重载不需要 `::` 指定全局**: 代码块身份:可直接照写示例 ```tsl grid := new GridData(); writeLn(mcols(grid)); type GridData = class public fa; function create(); begin fa := array(("A": 1, "B": 2, "C": 3), ("A": 5, "B": 5, "C": 5)); end; function operator mcols(); begin return mcols(fa); end; end; ``` 结果说明: - 输出 `3`:`mcols(grid)` 被对象的 `operator mcols()` 接管,返回内部 `fa` 的列数 - 关键字函数重载与二进制函数重载写法一致,但类内调用同名关键字函数不需要 `::` 前缀 ## 本页不生成的范围 - 多级 `[]` 下标重载 - 右侧算术如 `value + obj` 这些名称只作为边界提示,不作为本页可生成模板。 ## 禁止项 - 不要从本页 `operator` 示例外推未写入文档的重载族。 - 重载 `::` / `:.` 遍历时,不要漏掉配套的 `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex` 重载,否则遍历体会报 `override function not found`。 - 不要把多级 `[]` 下标重载或 `value + obj` 这类右侧算术写成文档事实。 - 不要在本页发明普通类语法;基础对象模型回 [08_objects_and_classes.md](08_objects_and_classes.md)。