playbook/docs/tsl/syntax/31_complex_and_weakref.md

329 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Complex And WeakRef
文档类型:语法主线
是否可直接用于生成代码:仅部分
是否含已验证可执行示例:是
是否含已验证反例:是
遇到不确定时跳转到:[09_objects_and_classes.md](09_objects_and_classes.md)、[32_object_overloads_and_iteration.md](32_object_overloads_and_iteration.md)、[12_pitfalls.md](12_pitfalls.md)
手册位置:第 31 篇,共 32 篇。上一篇:[30_runtime_services_and_global_cache.md](30_runtime_services_and_global_cache.md)。下一篇:[32_object_overloads_and_iteration.md](32_object_overloads_and_iteration.md)。
这一篇只讲当前解释器下已经实际跑通的两组新一代语言能力:复数,以及弱引用 / 自动弱引用里当前真正可靠的最小写法。
## 这一篇解决什么问题
回答“复数现在到底怎样写,弱引用到底有哪些语法真的能用,哪些历史资料里的写法今天不能直接照抄”。
## 必须记住的规则
- 复数字面量可以直接写成 `a + bj`,也可以用 `complex(a, b)` 构造。
- `datatype(z)` 对复数返回 `41``ifcomplex(z)` 对复数返回 `1`
- `real`、`imag`、`conj`、`abs` 当前都已验证可用。
- 当前解释器里,实数 `x` 与复数 `x + 0j` 的相等比较结果为真。
- `complex(array(...), imag)` 会返回 `Array``complex(fmarray..., imag)` 会返回 `FMArray`,并且其单元格类型是 `41`
- 弱引用能力的条件编译宏当前已验证是 `weakptr`;自动弱引用相关宏是 `AutoWeak`
- `weakref(obj)` 可以创建弱引用;对象仍存活时,`weakref_get(w)` 可以拿回强引用。
- `checkweakref(w)` 当前已验证:对象仍存活时返回 `1`,对象已释放后返回 `-1`
- 对已经失效的弱引用直接调用 `weakref_get(w)`,当前解释器里会运行报错,不要把它当成“安全返回 nil”的接口。
- 类成员上的 `[WeakRef] field;``[AutoRef] field;` 当前已验证可以通过。
- `[WeakRef]` 成员不会阻止对象析构;强引用释放后,对象会正常销毁。
- 历史资料里的类内段落式 `WeakRef;` / `AutoRef;` 在当前解释器里没有通过,错误信息包含 `invalid class definition`
## 已验证语法
### 复数常量、类型与基础读取
代码块身份:已验证可执行示例
```tsl
program test;
begin
z1 := 4 + 3j;
z2 := complex(5, -2);
WriteLn(datatype(z1));
WriteLn(ifcomplex(z1));
WriteLn(real(z1));
WriteLn(imag(z1));
WriteLn(z2);
end.
```
已验证运行结果:
- `datatype(4 + 3j)` 返回 `41`
- `ifcomplex(4 + 3j)` 返回 `1`
- `real(4 + 3j)` 返回 `4`
- `imag(4 + 3j)` 返回 `3`
- `complex(5, -2)` 打印结果是 `5-2j`
### 共轭、模与等值比较
代码块身份:已验证可执行示例
```tsl
program test;
begin
z := 4 + 3j;
c := conj(z);
WriteLn(abs(z));
WriteLn(real(c));
WriteLn(imag(c));
if 3.15 = 3.15 + 0j then
WriteLn(1);
else
WriteLn(0);
end.
```
已验证运行结果:
- `abs(4 + 3j)` 返回 `5`
- `conj(4 + 3j)` 的实部是 `4`、虚部是 `-3`
- `3.15 = 3.15 + 0j` 当前比较结果为真
### 复数 `Array` 与复数 `FMArray`
代码块身份:已验证可执行示例
```tsl
program test;
begin
a := complex(array(1, 2, 3), 5.5);
f := complex(fmarray[1, 2, 3], 5.5);
WriteLn(datatype(a));
WriteLn(length(a));
WriteLn(a[0], ',', a[1], ',', a[2]);
WriteLn(datatype(f));
WriteLn(datatype(f, 1));
WriteLn(length(f));
WriteLn(f[0], ',', f[1], ',', f[2]);
end.
```
已验证运行结果:
- `complex(array(1, 2, 3), 5.5)``datatype``5`
- 上述复数数组长度是 `3`,三个元素依次是 `1+5.5j`、`2+5.5j`、`3+5.5j`
- `complex(fmarray[1, 2, 3], 5.5)``datatype``27`
- 上述复数 `FMArray` 的单元格类型 `datatype(f, 1)``41`
-`FMArray` 长度是 `3`,三个元素依次是 `1+5.5j`、`2+5.5j`、`3+5.5j`
### 弱引用能力的条件编译判定
代码块身份:已验证可执行示例
```tsl
program test;
begin
{$IFDEF weakptr}
WriteLn('weakptr');
{$ELSE}
WriteLn('Noweakptr');
{$ENDIF}
{$IFDEF AutoWeak}
WriteLn('AutoWeak');
{$ELSE}
WriteLn('NoAutoWeak');
{$ENDIF}
end.
```
已验证运行结果:
- 当前解释器会输出 `weakptr`
- 当前解释器也会输出 `AutoWeak`
- 说明这代解释器里,弱引用能力的条件编译开关应写成 `weakptr`,自动弱引用能力的开关应写成 `AutoWeak`
### `weakref`、`weakref_get` 与 `checkweakref`
对象仍然存活时:
代码块身份:已验证可执行示例
```tsl
program test;
type TNode = class
name;
function create(v);
begin
name := v;
end;
end;
begin
a := new TNode('A');
w := weakref(a);
WriteLn(checkweakref(w));
s := weakref_get(w);
WriteLn(s.name);
end.
```
已验证运行结果:
- `checkweakref(w)` 返回 `1`
- `weakref_get(w)` 能拿回强引用
- 通过拿回的强引用可继续访问对象成员,此处输出 `A`
沿用同一个 `TNode`,只把主体改成下面这样:
代码块身份:配置片段 / 概念骨架
```tsl
program test;
begin
a := new TNode('A');
w := weakref(a);
WriteLn(checkweakref(w));
a := nil;
WriteLn(checkweakref(w));
end.
```
已验证运行结果:
- 创建后 `checkweakref(w)` 返回 `1`
- 把最后一个强引用设为 `nil` 后,`checkweakref(w)` 返回 `-1`
当前还额外验证到:
代码块身份:反例 / 不可照写
```text
a := new TNode('A');
w := weakref(a);
a := nil;
t := weakref_get(w);
```
上面这种写法在当前解释器里会运行报错,因此访问弱引用前应先做 `checkweakref(w)` 判定。
### `[WeakRef]` 与 `[AutoRef]` 成员标记
成员级标记当前可以直接写在类里:
代码块身份:已验证可执行示例
```tsl
program test;
type TChild = class
public
[WeakRef] owner1;
[AutoRef] owner2;
end;
begin
WriteLn(1);
end.
```
已验证运行结果:
- 上述写法可以正常执行,并输出 `1`
- 说明 `[WeakRef] field;``[AutoRef] field;` 当前都能通过
`[WeakRef]` 成员不会阻止对象析构:
代码块身份:已验证可执行示例
```tsl
program test;
type TA = class
public
function destroy();
begin
WriteLn('Destroy');
end;
end;
type TB = class
public
[WeakRef] owner;
function Bind(v);
begin
owner := v;
end;
end;
begin
a := new TA();
b := new TB();
b.Bind(a);
a := nil;
WriteLn('AfterNil');
end.
```
已验证运行结果:
- 输出顺序是 `Destroy`、`AfterNil`
- 说明 `TB.owner` 作为 `[WeakRef]` 成员,不会把 `TA` 实例继续强持有
## 暂不在本页展开的部分
- 复数统计函数、分解函数与更大函数族
- `MakeWeakRef`、`MakeStrongRef`、`weakref_check` 等别名接口
- `property write` 传播自动弱引用的更复杂规则
- 循环引用场景里的完整模式化写法
## 最小可编译示例
复数最短骨架:
代码块身份:已验证可执行示例
```tsl
program test;
begin
z := 4 + 3j;
WriteLn(real(z));
WriteLn(imag(z));
end.
```
弱引用最短骨架:
代码块身份:已验证可执行示例
```tsl
program test;
type TNode = class
name;
function create(v);
begin
name := v;
end;
end;
begin
a := new TNode('A');
w := weakref(a);
WriteLn(checkweakref(w));
end.
```
## 常见误写
- 把弱引用能力的条件编译宏写成 `WeakRef`
- 以为 `weakref_get(deadWeakRef)` 会像普通可空访问那样安全返回 `nil`
- 以为历史资料里的类内段落式 `WeakRef;` / `AutoRef;` 现在也能直接通过。
- 以为 `[WeakRef]` 成员会继续强持有对象。
代码块身份:反例 / 不可照写
```text
type TChild = class
public
owner0;
[WeakRef] owner1;
WeakRef;
owner2;
AutoRef;
owner3;
end;
```
上面这种把 `WeakRef;` / `AutoRef;` 当作类内段落切换的写法,在当前解释器里会编译失败,错误信息包含 `invalid class definition`
## 跳转指引
- 回看语法主入口:见 [index.md](index.md)
- 回看对象主线:见 [09_objects_and_classes.md](09_objects_and_classes.md)
- 看对象重载:见 [32_object_overloads_and_iteration.md](32_object_overloads_and_iteration.md)