Files
playbook/skills/tsl-syntax-reference/references/23_object_overloads_and_iteration.md
T
2026-07-29 07:58:05 +00:00

16 KiB
Raw Blame History

TSL 对象重载与迭代

这一篇只讲对象直接参与语言级操作的文档能力:基础算符重载、[] 重载、for in 重载,以及 mrows / mcols / msize 这类矩阵关键字重载。

本篇职责

回答“当类不只是普通对象,而要直接参与 obj + xobj[index]for v in objmrows(obj) 这类语言级操作时,支持哪些文档明确写法”。

核心规则

  • 对象二元算符重载的最小可靠形态是成员方法 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 的重载本页已给出可照写形态,照本页示例写即可。

可直接照写示例

二元算符重载

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

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;

结果说明:

  • 依次输出 20011
  • 说明 obj + value 可以通过成员 operator + 接管
  • 说明带 is_left 的比较算符可以同时处理 obj < valuevalue < obj
  • 本例私有成员是 real_part_ / imaginary_part_,对外通过 property 暴露(命名风格本身不属于本页事实)

代码块身份:输出片段

20
0
1
1

[] 重载:operator[] / operator[1]

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

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;

结果说明:

  • 依次输出 3999
  • 说明 operator[]operator[1] 可以完成单层下标读取和写入

[] 重载:operator[0] / operator[1]

读取签名也可以写成 function operator[0](index, s1);(与上一段 operator[] 等价的另一组写法):

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

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[0](index, s1);
    begin
        return data[index];
    end;
    function operator[1](index, v);
    begin
        data[index] := v;
    end;
end;

结果说明:

  • 依次输出 3999
  • 说明 operator[0] / operator[1] 这组写法在单层下标场景同样可用

for in 重载

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

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;

结果说明:

  • 单变量循环依次输出 ABC
  • 双变量循环依次输出 0:A1:B2:C
  • 说明 operator for(flag) 可以重载 for in
  • 也说明同一个对象可以按返回值形态同时支持“单变量遍历”和“索引 + 值遍历”

mrows / mcols / msize 重载(带参形态,支持下标列表)

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

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;

结果说明:

  • 依次输出 2323ABC
  • 说明 mrows(obj)mcols(obj)msize(obj) 都可由对象重载接管
  • 说明对象方法式调用 obj.mcols(1) 也可继续取得列下标列表

++ / += / -- / -= 自增自减重载

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

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;

结果说明:

  • 依次输出 11111217
  • 前置 ++counterv0 分支,直接原地修改对象状态
  • 后置 counter++v = 0 分支:分支构造并返回递增后的新对象,运行时用它更新 counter 本身,而后置表达式的取值是递增前的原对象(所以 c.data11counter.data12
  • 说明 operator += (v) 可以接管 counter += 5

---= 与之对称:

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

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(后置)分支构造并返回递减后的新对象供运行时更新变量本身,后置表达式的取值是递减前的原对象;v0(前置)时原地递减

二进制函数重载:operator funcName

除了符号算符,operator 还能重载具名的全局二进制函数(如 DateToStrTryStrToInt 等)。定义写成 [class] function operator funcName(...)class 关键字可选,加上表示类方法,不加表示成员函数。

成员函数重载(参数比原函数少 1 个,用第一个参数的对象实例调用):

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

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,参数与原函数一致):

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

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) 作为类方法接管调用,参数个数与原二进制函数一致

类内用 :: 调同名全局函数(避免重载递归):

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

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)ret1(转换成功),出参 msg314
  • 重载的分派按实参类型决定:本类对象走重载版本,非对象实参(如 value 是字符串)走全局版本
  • 因此当被重载的实参已经不是本类对象(如上面 DateToStr 两例把对象拆成基础类型再调用)时,不加 :: 也不会递归;只有当传给同名函数的实参仍是本类对象时才会递归回自己,这时用 :: 前缀强制指定全局版本
  • 重载函数支持通过参数传出返回值(msg 作为出参被赋值)

:: / :. 遍历重载与 mcell / mrow / mcol / mIndexCount / mIndex

重载 ::(二维遍历)或 :.(深度遍历)后,对象就能像矩阵一样被 obj::begin ... end 遍历。遍历体里用到的 mcell / mrow / mcol / mIndexCount / mIndex(n) 也各自重载,返回当前单元的值、行下标、列下标、维度数和第 n 维下标。operator ::(flag)flag0 表示第一次循环、1 表示后续循环,返回 0nil 结束遍历、返回非零数字继续:

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

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

mrows / mcols / msize 重载(无参形态,只取数量且免 ::

msize / mrows / mcols 这类关键字函数也能重载,形态同二进制函数重载 [class] function operator KeyWord(...),但关键字重载不需要 :: 指定全局

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

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;

结果说明:

  • 输出 3mcols(grid) 被对象的 operator mcols() 接管,返回内部 fa 的列数
  • 关键字函数重载与二进制函数重载写法一致,但类内调用同名关键字函数不需要 :: 前缀

本页不生成的范围

  • 多级 [] 下标重载
  • 右侧算术如 value + obj

这些名称只作为边界提示,不作为本页可生成模板。

禁止项

  • 不要从本页 operator 示例外推未写入文档的重载族。
  • 重载 :: / :. 遍历时,不要漏掉配套的 mcell / mrow / mcol / mIndexCount / mIndex 重载,否则遍历体会报 override function not found
  • 不要把多级 [] 下标重载或 value + obj 这类右侧算术写成文档事实。
  • 不要在本页发明普通类语法;基础对象模型事实见 08_objects_and_classes.md