🎨 style(syntax): normalize naming conventions

Align all TSL syntax documentation examples with docs/tsl/naming.md:

- Classes/types: PascalCase, drop Hungarian prefix (THuman→Human)
- Parameters/locals: snake_case with meaningful names (isLeft→is_left, maxb→max_b)
- Private members: snake_case_ with trailing underscore (real_part_, imaginary_part_)
- Public members: PascalCase (value→Value)
- Top-level functions: PascalCase (test→Test)
- Module constants: kPascalCase (kernel_dll→kKernelDll)

Affected: 01-24 syntax docs (23 files)
Verified: 150+ code blocks locally tested, output unchanged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
csh
2026-07-07 16:34:19 +08:00
co-authored by Claude Fable 5
parent 23c35fdfda
commit 014c23d386
23 changed files with 2009 additions and 352 deletions
+4 -4
View File
@@ -90,9 +90,9 @@ a := 1;
```tsl
a := 1;
test();
Test();
function test();
function Test();
begin
echo "test";
end;
@@ -135,13 +135,13 @@ end;
代码块身份:可直接照写示例
```tsl
function Test1();
function TestFunc();
begin
echo "test1";
end;
```
代码块说明:这个 `.tsf` 部署到解释器 `funcext` 后,`.tsl` 脚本可以直接调用 `Test1();`。部署方式属于项目执行层,不写进通用语法页。
代码块说明:这个 `.tsf` 部署到解释器 `funcext` 后,`.tsl` 脚本可以直接调用 `TestFunc();`。部署方式属于项目执行层,不写进通用语法页。
代码块身份:输出片段
+5 -5
View File
@@ -66,9 +66,9 @@ a := 1;
```tsl
a := 1;
test();
Test();
function test();
function Test();
begin
echo "test";
end;
@@ -88,11 +88,11 @@ test
```tsl
obj := new MyClass();
obj.value := 5;
echo obj.value;
obj.Value := 5;
echo obj.Value;
type MyClass = class
value;
Value;
end;
```
+123
View File
@@ -377,6 +377,129 @@ writeLn(s);
- 输出 `AAA111BBB`
- 这说明 `s[index:0] := ...` 是在指定位置插入,而不是删除或替换区间
### 字符串 `$` 连接与拼装
`$` 用于字符串连接和类型转字符串拼装:
代码块身份:可直接照写示例
```tsl
s1 := "abc" $ "def";
a := 1;
b := 2.34;
c := "AAA";
d := "A=" $ a $ " B=" $ b $ " C=" $ c;
writeLn(s1);
writeLn(d);
```
代码块身份:输出片段
```text
abcdef
A=1 B=2.34 C=AAA
```
说明:
- `"abc" $ "def"` 连接两个字符串得到 `"abcdef"`
- 整数、实数等类型可以直接用 `$` 拼装成字符串
- 拼接实数时,保留最高精度,表现与 `floattostr` 一致
- 纯小数可能走科学计数法:`"_" $ 0.0000005 $ ","` 返回 `"_5E-7,"`
- 带整数部分的小数保留精度:`"_" $ 1.0000005 $ ","` 返回 `"_1.0000005,"`
### 字符串 `like` 模式匹配
`like` 用于判断字符串是否符合指定模式(支持通配符和正则表达式):
代码块身份:可直接照写示例
```tsl
result1 := "hello" like "he*";
result2 := "HELLO" like "he*";
result3 := "test@example.com" like "*@*";
writeLn("result1:", result1);
writeLn("result2:", result2);
writeLn("result3:", result3);
```
代码块身份:输出片段
```text
result1: 1
result2: 0
result3: 1
```
说明:
- `like` 大小写敏感,`"hello" like "he*"` 返回 `1`(真),`"HELLO" like "he*"` 返回 `0`(假)
- `*` 是通配符,匹配任意字符序列
- `like` 也支持正则表达式模式(如 `"\\d{4}-\\d{2}-\\d{2}"` 匹配日期格式)
`not like` 是取反形式(TSL 2025/8 版本起支持):
代码块身份:可直接照写示例
```tsl
result := "abc" not like "xyz*";
writeLn(result);
```
代码块身份:输出片段
```text
1
```
### `format` 格式化占位符
`format` 用于按占位符格式化输出:
代码块身份:可直接照写示例
```tsl
result := format("%d-%s", 7, "x");
writeLn(result);
```
代码块身份:输出片段
```text
7-x
```
说明:
- `%d` 是整数占位符
- `%s` 是字符串占位符
- `format` 的完整占位符规范见 [../codegen/builtin/basic.md](../codegen/builtin/basic.md)
### `formatdatetime` 日期格式化
`formatdatetime` 用于将日期按指定格式输出:
代码块身份:可直接照写示例
```tsl
dt := strtodate("2024-01-02");
result := formatdatetime("yyyy-mm-dd", dt);
writeLn(result);
```
代码块身份:输出片段
```text
2024-01-02
```
说明:
- `yyyy` 表示四位年份
- `mm` 表示两位月份
- `dd` 表示两位日期
- `formatdatetime` 的完整格式说明见 [../codegen/builtin/basic.md](../codegen/builtin/basic.md)
## 默认生成模板
如果你只是要抓住“基本类型 + array”的第一层,用这个最短例子:
+11 -11
View File
@@ -100,8 +100,8 @@ writeLn(a);
代码块身份:可直接照写示例
```tsl
const value = 1;
echo value;
const kValue = 1;
echo kValue;
```
代码块身份:输出片段
@@ -115,8 +115,8 @@ echo value;
代码块身份:可直接照写示例
```tsl
const value = 1 + 2 * 3;
writeLn(value);
const kValue = 1 + 2 * 3;
writeLn(kValue);
```
代码块身份:输出片段
@@ -146,14 +146,14 @@ unit DemoUnit;
interface
const value = 1;
const kValue = 1;
function GetValue();
implementation
function GetValue();
begin
return value;
return kValue;
end;
end.
@@ -166,7 +166,7 @@ end.
```tsl
type DemoType = class
public
const value = 1;
const kValue = 1;
end;
```
@@ -175,8 +175,8 @@ end;
代码块身份:可直接照写示例
```tsl
const max_retries = 3 + 4;
value := max_retries;
const kMaxRetries = 3 + 4;
value := kMaxRetries;
```
### 多参数赋值
@@ -311,8 +311,8 @@ end;
代码块身份:可直接照写示例
```tsl
const max_retries = 3;
counter := max_retries;
const kMaxRetries = 3;
counter := kMaxRetries;
items := array(1, 2, 3);
```
+3 -3
View File
@@ -83,9 +83,9 @@
```tsl
a := 1;
test();
Test();
function test();
function Test();
begin
echo "test";
end;
@@ -737,7 +737,7 @@ end;
```tsl
f := thisFunction(Add);
writeLn(Call(f, 3, 4));
writeLn(call(f, 3, 4));
function Add(a, b);
begin
+10 -10
View File
@@ -103,10 +103,10 @@ writeLn(sum);
代码块身份:可直接照写示例
```tsl
s := 0;
sum := 0;
for i := 1 to 5 step 2 do
s := s + i;
writeLn(s);
sum := sum + i;
writeLn(sum);
```
输出说明:
@@ -124,10 +124,10 @@ writeLn(s);
代码块身份:可直接照写示例
```tsl
s := 0;
sum := 0;
for i := 5 downto 1 step 2 do
s := s + i;
writeLn(s);
sum := sum + i;
writeLn(sum);
```
输出说明:
@@ -145,15 +145,15 @@ writeLn(s);
代码块身份:可直接照写示例
```tsl
data := array(10, 20, 30);
for i, v in data do
writeLn(i * 100 + v);
numbers := array(10, 20, 30);
for i, value in numbers do
writeLn(i * 100 + value);
```
输出说明:
- 依次输出 `10``120``230`
- 这说明 `for i, v in data` 里的 `i``0` 开始
- 这说明 `for i, value in numbers` 里的 `i``0` 开始
代码块身份:输出片段
+165 -97
View File
@@ -192,29 +192,29 @@ writeLn(a);
代码块身份:可直接照写示例
```tsl
class(THuman).mCount := 100;
writeLn(class(THuman).mCount);
h := new THuman();
writeLn(class(THuman).mCount);
writeLn(h.mCount);
class(Human).count := 100;
writeLn(class(Human).count);
h := new Human();
writeLn(class(Human).count);
writeLn(h.count);
type THuman = class
type Human = class
public
static mCount;
static count;
function create();
begin
mCount := (mCount ?: 0) + 1;
count := (count ?: 0) + 1;
end;
end;
```
输出说明:
- `class(THuman).mCount := 100` 可以直接写静态字段
- `class(THuman).mCount` 先输出 `100`
- 创建对象后,`class(THuman).mCount` 输出 `101`
- 通过实例读取 `h.mCount` 也输出 `101`
- 单个静态字段默认写成 `static mCount;`
- `class(Human).count := 100` 可以直接写静态字段
- `class(Human).count` 先输出 `100`
- 创建对象后,`class(Human).count` 输出 `101`
- 通过实例读取 `h.count` 也输出 `101`
- 单个静态字段默认写成 `static count;`
`const` 成员:
@@ -224,20 +224,20 @@ end;
o := new C();
writeLn(o.TestConst());
writeLn(o.TestConstInParam());
writeLn(o.mA);
writeLn(class(C).mB);
writeLn(o.a);
writeLn(class(C).b);
type C = class
public
const mA = 1;
static const mB = mA + 10;
const a = 1;
static const b = a + 10;
function TestConst();
begin
return mA + mB;
return a + b;
end;
function TestConstInParam(b = mB);
function TestConstInParam(param_b = b);
begin
return b;
return param_b;
end;
end;
```
@@ -431,53 +431,53 @@ end;
代码块身份:可直接照写示例
```tsl
b := new MyBox();
b.Value := 7;
writeLn(b.Value);
b.Value := -1;
writeLn(b.Value);
box := new Box();
box.Value := 7;
writeLn(box.Value);
box.Value := -1;
writeLn(box.Value);
type MyBox = class
type Box = class
public
_value;
value_;
function SetValue(v);
begin
if v > 0 then
_value := v;
value_ := v;
end;
property Value read _value write SetValue;
property Value read value_ write SetValue;
end;
```
输出说明:
- `b.Value := 7` 后,`b.Value` 输出 `7`
- `b.Value := -1` 后,`b.Value` 仍输出 `7`
- `box.Value := 7` 后,`box.Value` 输出 `7`
- `box.Value := -1` 后,`box.Value` 仍输出 `7`
带类型注解的 `property`
代码块身份:可直接照写示例
```tsl
b := new MyBox();
b.Value := 9;
writeLn(b.Value);
box := new Box();
box.Value := 9;
writeLn(box.Value);
type MyBox = class
type Box = class
public
_value;
value_;
function SetValue(v);
begin
_value := v;
value_ := v;
end;
property Value: integer read _value write SetValue;
property Value: integer read value_ write SetValue;
end;
```
输出说明:
- `property Value: integer ...` 这种类型注解写法可以通过
- 上述例子中的 `b.Value` 输出 `9`
- 上述例子中的 `box.Value` 输出 `9`
字段和类方法类型注解:
@@ -491,10 +491,10 @@ writeLn(box.ReadName());
type TypedBox = class
public
function create(_name: string; _value: any);
function create(name: string; value: any);
begin
name_ := _name;
value_ := _value;
name_ := name;
value_ := value;
end;
function ReadName(): string;
begin
@@ -511,7 +511,7 @@ end;
输出说明:
- `name_: string;``value_: any;` 可以作为类字段类型注解。
- `function create(_name: string; _value: any);` 可以作为类方法参数类型注解。
- `function create(name: string; value: any);` 可以作为类方法参数类型注解。
- `function ReadName(): string;` 可以作为类方法返回值类型注解。
- 上述例子依次输出 `abc``7``abc`
@@ -538,8 +538,8 @@ writeLn(b.ReadLeft());
type PairBox = class
public
function create(_left: string); overload;
function create(_left: string; _right: any); overload;
function create(left: string); overload;
function create(left: string; right: any); overload;
function ReadLeft(): string;
property Left: string read left_ write left_;
property Right: any read right_ write right_;
@@ -548,15 +548,15 @@ private
right_: any;
end;
function PairBox.create(_left: string); overload;
function PairBox.create(left: string); overload;
begin
create(_left, nil);
create(left, nil);
end;
function PairBox.create(_left: string; _right: any); overload;
function PairBox.create(left: string; right: any); overload;
begin
left_ := _left;
right_ := _right;
left_ := left;
right_ := right;
end;
function PairBox.ReadLeft(): string;
@@ -570,7 +570,7 @@ end;
- 类内可以只声明带类型的重载方法签名。
- 类外实现写成 `function PairBox.create(...); overload;`,并保持同样的参数类型和 `overload` 标记。
- 类外实现属于声明区;写完后不要再追加脚本语句。
- 从一个构造函数转调另一个构造函数时,直接写 `create(_left, nil);`,不要加 `self` 前缀。
- 从一个构造函数转调另一个构造函数时,直接写 `create(left, nil);`,不要加 `self` 前缀。
- 上述例子依次输出 `left``<NIL>``left``2``left`
代码块身份:输出片段
@@ -588,9 +588,9 @@ left
代码块身份:可直接照写示例
```tsl
aa := new A();
aa.idx(0) := "abc";
writeLn(aa.idx(0));
obj := new A();
obj.idx(0) := "abc";
writeLn(obj.idx(0));
type A = class
public
@@ -613,18 +613,18 @@ end;
输出说明:
- `aa.idx(0) := "abc"` 可以写入索引 property
- `aa.idx(0)` 输出 `abc`
- `obj.idx(0) := "abc"` 可以写入索引 property
- `obj.idx(0)` 输出 `abc`
固定 `index` property
代码块身份:可直接照写示例
```tsl
aa := new A();
aa.idx0 := "abc";
writeLn(aa.idx0);
writeLn(aa.idx(0));
obj := new A();
obj.idx0 := "abc";
writeLn(obj.idx0);
writeLn(obj.idx(0));
type A = class
public
@@ -648,19 +648,19 @@ end;
输出说明:
- `aa.idx0 := "abc"` 可以写入固定整数索引 property
- `aa.idx0` 输出 `abc`
- `aa.idx(0)` 也输出 `abc`
- `obj.idx0 := "abc"` 可以写入固定整数索引 property
- `obj.idx0` 输出 `abc`
- `obj.idx(0)` 也输出 `abc`
固定字符串索引:
代码块身份:可直接照写示例
```tsl
aa := new A();
aa.school := "math";
writeLn(aa.school);
writeLn(aa.idx("High school"));
obj := new A();
obj.school := "math";
writeLn(obj.school);
writeLn(obj.idx("High school"));
type A = class
public
@@ -685,8 +685,8 @@ end;
输出说明:
- `property school index "High school"` 这种固定字符串索引写法可以通过
- `aa.school` 输出 `math`
- `aa.idx("High school")` 也输出 `math`
- `obj.school` 输出 `math`
- `obj.idx("High school")` 也输出 `math`
参数化 `property`
@@ -699,18 +699,18 @@ writeLn(d.DateV());
type MyDate = class
public
_year;
_month;
_day;
year_;
month_;
day_;
function getDateV();
begin
return _year * 10000 + _month * 100 + _day;
return year_ * 10000 + month_ * 100 + day_;
end;
function setDateV(y, m, d);
begin
_year := y;
_month := m;
_day := d;
year_ := y;
month_ := m;
day_ := d;
end;
property DateV(y, m) read getDateV write setDateV;
end;
@@ -726,9 +726,9 @@ end;
代码块身份:可直接照写示例
```tsl
aa := new A();
aa.Item(2) := "x";
writeLn(aa.Item(2));
obj := new A();
obj.Item(2) := "x";
writeLn(obj.Item(2));
type A = class
public
@@ -751,9 +751,9 @@ end;
输出说明:
- `read getItem` 这种读方法接同参数个数”的写法可以通过
- `write setItem` 这种写方法接参数个数 + 赋值值”的写法可以通过
- 上述例子中的 `aa.Item(2)` 输出 `x`
- `read getItem` 这种读方法接同参数个数”的写法可以通过
- `write setItem` 这种写方法接参数个数 + 赋值值”的写法可以通过
- 上述例子中的 `obj.Item(2)` 输出 `x`
### 对象创建与类类型
@@ -922,9 +922,9 @@ public
begin
return p1 + p2;
end;
function fun(p1); overload;
function fun(param): integer; overload;
begin
return p1 + 10;
return param + 10;
end;
end;
```
@@ -1050,6 +1050,74 @@ end;
- 父类 `virtual` + 子类 `override` 组合可以通过
- 上述例子中的 `d.Speak()` 输出 `2`
子类同名方法不加 `override` 时是「隐藏(hide)」而不是「覆盖(override)」:
代码块身份:可直接照写示例
```tsl
c := new Child();
writeLn(c.Who());
writeLn(c.Ask());
type Parent = class
public
function Who();
begin
return "parent";
end;
function Ask();
begin
return Who();
end;
end;
type Child = class(Parent)
public
function Who();
begin
return "child";
end;
end;
```
输出说明:
- `c.Who()` 输出 `child`:直接调用命中子类自己的同名方法
- `c.Ask()` 输出 `parent`:父类方法内部的 `Who()` 仍解析到父类版本,说明不加 `override` 只是「隐藏」父类方法,没有改变父类内部的调用目标
- 要让父类方法内部也定向到子类实现,父类方法须声明 `virtual`、子类方法须声明 `override`
对照:父类 `virtual` + 子类 `override` 时,父类方法内部调用会定向到子类:
代码块身份:可直接照写示例
```tsl
c := new Child();
writeLn(c.Ask());
type Parent = class
public
function Who(); virtual;
begin
return "parent";
end;
function Ask();
begin
return Who();
end;
end;
type Child = class(Parent)
public
function Who(); override;
begin
return "child";
end;
end;
```
输出说明:
- `c.Ask()` 输出 `child``virtual` + `override` 后,父类方法内部的 `Who()` 定向到子类实现
- 这就是 hide 与 override 的关键区别:hide 只影响直接调用,override 改变了所有经由基类的间接调用
`class(BaseClass, ObjectName).MethodName()`
代码块身份:可直接照写示例
@@ -1179,35 +1247,35 @@ end;
代码块身份:可直接照写示例
```tsl
h := new THuman();
writeLn(class(THuman).GetCount());
h := new Human();
writeLn(class(Human).GetCount());
h := nil;
writeLn(class(THuman).GetCount());
writeLn(class(Human).GetCount());
type THuman = class
type Human = class
public
static mCount;
static count;
function create();
begin
mCount := (mCount ?: 0) + 1;
count := (count ?: 0) + 1;
end;
function destroy();
begin
mCount--;
writeLn(mCount);
count--;
writeLn(count);
end;
class function GetCount();
begin
return mCount;
return count;
end;
end;
```
输出说明:
- 创建对象后,`class(THuman).GetCount()` 输出 `1`
- 创建对象后,`class(Human).GetCount()` 输出 `1`
- `h := nil` 时会触发 `destroy()`,中途输出 `0`
- 释放后再次读取 `class(THuman).GetCount()` 也输出 `0`
- 释放后再次读取 `class(Human).GetCount()` 也输出 `0`
`self(0)` / `self(1)`
@@ -1373,10 +1441,10 @@ cls := class(Unit1.Class1.Class2);
```text
MathBox.Add(1, 2);
THuman.mCount := 7;
Human.count := 7;
```
上面这种裸类名成员访问不作为可写事实;类方法可用 `class(MathBox).Add(...)``findClass("MathBox").Add(...)` 调用,静态字段可用 `class(THuman).mCount` 访问。
上面这种裸类名成员访问不作为可写事实;类方法可用 `class(MathBox).Add(...)``findClass("MathBox").Add(...)` 调用,静态字段可用 `class(Human).count` 访问。
代码块身份:反例 / 不可照写
+8 -8
View File
@@ -245,7 +245,7 @@ interface
uses UnitB;
type TBox = class
type Box = class
public
function FromInterface();
begin
@@ -260,7 +260,7 @@ implementation
uses UnitC;
function TBox.FromImplementation();
function Box.FromImplementation();
begin
return FC();
end;
@@ -278,7 +278,7 @@ end.
// main.tsl
uses UnitA;
obj := new TBox();
obj := new Box();
writeLn(CallB());
writeLn(CallC());
writeLn(obj.FromInterface());
@@ -304,8 +304,8 @@ interface
function MakeValue();
type UnitBox = class
public
value;
function create(_value);
Value;
function create(init_value);
function ReadValue();
end;
@@ -315,13 +315,13 @@ function MakeValue();
begin
return 10;
end;
function UnitBox.create(_value);
function UnitBox.create(init_value);
begin
value := _value;
Value := init_value;
end;
function UnitBox.ReadValue();
begin
return value;
return Value;
end;
end.
+97 -38
View File
@@ -4,7 +4,7 @@
是否可直接用于生成代码:仅部分
是否含可直接照写示例:是
是否含不可照写反例:是
遇到不确定时:先按本页候选页继续判断;[05_functions_and_calls.md](05_functions_and_calls.md)、[06_expressions_and_operators.md](06_expressions_and_operators.md)、[14_ts_sql.md](14_ts_sql.md)、[15_debug_and_profiler.md](15_debug_and_profiler.md)、[19_namespace_libpath_and_unit_runtime.md](19_namespace_libpath_and_unit_runtime.md)、[21_builtin_runtime_objects.md](21_builtin_runtime_objects.md)、[../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
遇到不确定时:先按本页候选页继续判断;[05_functions_and_calls.md](05_functions_and_calls.md)、[06_expressions_and_operators.md](06_expressions_and_operators.md)、[14_ts_sql.md](14_ts_sql.md)、[15_debug_and_profiler.md](15_debug_and_profiler.md)、[19_namespace_libpath_and_unit_runtime.md](19_namespace_libpath_and_unit_runtime.md)、[21_builtin_runtime_objects.md](21_builtin_runtime_objects.md)、[../codegen/dotnet/datawarehouse/](../codegen/dotnet/datawarehouse/);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
这一篇只处理运行时环境参数、块环境 `with` 语句、`with` 后缀、`#` 网格调用、`timeout` 后缀、`dupvalue(...)` 和全局缓存,不处理任何金融业务语义。
@@ -36,11 +36,11 @@
- `with array(...)` 只在该次调用里临时覆盖对应键,调用结束后会恢复外部原值。
- 如果外部原值本来不存在,`with array(...)` 调用结束后,对应键会恢复成 `nil`
- 不要把上面的后缀 `with` 直接泛化成“任何本地函数调用后面都能接 `with array(...)`”;本地函数后缀 `with` 属于反例。
- 本页只收通用键的例子;像 `pn_stock()``pn_date()` 这类金融上下文参数,统一到 [../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md) 查函数事实。
- 本页只收通用键的例子;像 `pn_stock()``pn_date()` 这类金融上下文参数,统一到 [../codegen/dotnet/datawarehouse/](../codegen/dotnet/datawarehouse/) 查函数事实。
- 网格调用的最小写法是 `r := #Func(args);`
- 网格调用返回的不是最终值;用 `dupvalue(r)` 取回结果。
- `timeout N` 后缀可直接接在网格调用后面。
- 全局缓存函数的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md);本页只保留缓存引用的运行时行为示例。
- 全局缓存函数的参数规格见 [../codegen/special/pending/system/03_global_cache.md](../codegen/special/pending/system/03_global_cache.md) 和 [../codegen/builtin/system.md](../codegen/builtin/system.md);本页只保留缓存引用的运行时行为示例。
- 写入和读取全局缓存成功时返回 `1`
- 从全局缓存取出的值,`ifCache(v)` 会返回 `1`
- 一旦对取出的缓存值做本地写入,它会立刻实例化;写入后 `ifCache(v)` 返回 `0`
@@ -94,9 +94,9 @@ writeLn(getSysParam("a"));
```tsl
setSysParam("a", 1);
sys_param_values := array("a": 2, "b": 3);
param_values := array("a": 2, "b": 3);
with *, sys_param_values do
with *, param_values do
begin
writeLn(getSysParam("a"));
writeLn(getSysParam("b"));
@@ -112,6 +112,33 @@ writeLn(getSysParam("b"));
- 块后输出 `2``3`
- 说明 `with *` 会把传入键合并进当前系统参数上下文;不要把它当成自动恢复外层值的隔离块
`with *, SysParamArray do` 使用当前所有系统参数:
代码块身份:可直接照写示例
```tsl
setSysParam("a", 10);
setSysParam("b", 20);
with *, SysParamArray do
begin
writeLn(getSysParam("a"));
writeLn(getSysParam("b"));
end
```
代码块身份:输出片段
```text
10
20
```
说明:
- `SysParamArray` 是特殊关键字,表示引用当前所有系统参数
- `with *, SysParamArray do` 把当前系统参数作为块环境传入
- 这是服务端环境的常用写法,用于保持系统参数上下文
### 块环境 `with **`
代码块身份:可直接照写示例
@@ -119,9 +146,9 @@ writeLn(getSysParam("b"));
```tsl
setSysParam("a", 1);
setSysParam("b", 9);
sys_param_values := array("b": 4);
param_values := array("b": 4);
with **, sys_param_values do
with **, param_values do
begin
writeLn(getSysParam("a") = nil);
writeLn(getSysParam("b"));
@@ -189,6 +216,8 @@ writeLn(getSysParam("b") = nil);
### `#` 网格调用与 `dupvalue`
网格调用函数:
代码块身份:可直接照写示例
```tsl
@@ -211,6 +240,29 @@ end;
6
```
网格调用数组表达式:
代码块身份:可直接照写示例
```tsl
items := array(1, 2, 3, 4, 5);
grid_result := #(items * 2);
writeLn(tostn(dupvalue(grid_result)));
```
代码块身份:输出片段
```text
array(2,4,6,8,10)
```
说明:
- `#(表达式)` 可以对表达式进行网格计算
- `#(items * 2)` 返回网格句柄
- `dupvalue(grid_result)` 取回最终结果数组
- 网格计算常用于大规模数组的分布式并行运算
### 网格调用的 `timeout`
代码块身份:可直接照写示例
@@ -234,20 +286,27 @@ end;
代码块身份:可直接照写示例
```tsl
v1 := array(1, 2, 3);
writeLn(setGlobalCache("PB_TEST_GC_BASIC", v1));
writeLn(getGlobalCache("PB_TEST_GC_BASIC", v2));
writeLn(ifCache(v2));
writeLn(length(v2));
writeLn(v2[0], ',', v2[1], ',', v2[2]);
source_data := array(1, 2, 3);
writeLn(setGlobalCache("PB_TEST_GC_BASIC", source_data));
writeLn(getGlobalCache("PB_TEST_GC_BASIC", cached_data));
writeLn(ifCache(cached_data));
writeLn(length(cached_data));
writeLn(cached_data[0], ',', cached_data[1], ',', cached_data[2]);
```
结果说明:
- `setGlobalCache("PB_TEST_GC_BASIC", v1)` 返回 `1`
- `getGlobalCache("PB_TEST_GC_BASIC", v2)` 返回 `1`
- 取出的 `v2``ifCache(v2)` 返回 `1`
- `v2` 长度是 `3`,内容是 `1,2,3`
- `setGlobalCache("PB_TEST_GC_BASIC", source_data)` 返回 `1`
- `getGlobalCache("PB_TEST_GC_BASIC", cached_data)` 返回 `1`
- 取出的 `cached_data``ifCache(cached_data)` 返回 `1`
- `cached_data` 长度是 `3`,内容是 `1,2,3`
说明:
- TSL 标识符大小写无关,`setGlobalCache``SetGlobalCache` 等价
- 推荐使用小写 `setGlobalCache``getGlobalCache` 以保持一致性
- `setGlobalCache(key, value)` 写入缓存,可选第三参数指定过期时间(秒)
- `getGlobalCache(key, out_var)` 读取缓存到输出变量
### `checkGlobalCacheExpired`
@@ -255,16 +314,16 @@ writeLn(v2[0], ',', v2[1], ',', v2[2]);
```tsl
setGlobalCache("PB_TEST_GC_EXPIRE", array(1, 2, 3));
getGlobalCache("PB_TEST_GC_EXPIRE", v);
writeLn(checkGlobalCacheExpired(v));
getGlobalCache("PB_TEST_GC_EXPIRE", cache_ref);
writeLn(checkGlobalCacheExpired(cache_ref));
setGlobalCache("PB_TEST_GC_EXPIRE", array(1, 2, 3, 4));
writeLn(checkGlobalCacheExpired(v));
writeLn(checkGlobalCacheExpired(cache_ref));
```
结果说明:
- 刚取出的缓存引用上,`checkGlobalCacheExpired(v)` 返回 `0`
- 同名缓存被重新设置后,旧引用上的 `checkGlobalCacheExpired(v)` 返回 `1`
- 刚取出的缓存引用上,`checkGlobalCacheExpired(cache_ref)` 返回 `0`
- 同名缓存被重新设置后,旧引用上的 `checkGlobalCacheExpired(cache_ref)` 返回 `1`
### 写入后会实例化
@@ -272,17 +331,17 @@ writeLn(checkGlobalCacheExpired(v));
```tsl
setGlobalCache("PB_TEST_GC_DETACH", array(1, 2, 3));
getGlobalCache("PB_TEST_GC_DETACH", v);
writeLn(ifCache(v));
v[0] := 100;
writeLn(ifCache(v));
writeLn(v[0], ',', v[1], ',', v[2]);
getGlobalCache("PB_TEST_GC_DETACH", local_copy);
writeLn(ifCache(local_copy));
local_copy[0] := 100;
writeLn(ifCache(local_copy));
writeLn(local_copy[0], ',', local_copy[1], ',', local_copy[2]);
```
结果说明:
- 刚取出时 `ifCache(v)` 返回 `1`
- 对 `v[0]` 赋值后,`ifCache(v)` 立即返回 `0`
- 刚取出时 `ifCache(local_copy)` 返回 `1`
- 对 `local_copy[0]` 赋值后,`ifCache(local_copy)` 立即返回 `0`
- 写入后的本地值内容是 `100,2,3`
### 全局缓存参与 `select`
@@ -290,13 +349,13 @@ writeLn(v[0], ',', v[1], ',', v[2]);
代码块身份:可直接照写示例
```tsl
src := array((1, 2), (3, 4), (2, 1));
setGlobalCache("PB_TEST_GC_SELECT", src);
getGlobalCache("PB_TEST_GC_SELECT", v);
q := select * from v order by [0] desc end;
writeLn(dataType(q));
writeLn(mrows(q));
writeLn(q[0][0], ',', q[0][1], ';', q[1][0], ',', q[1][1], ';', q[2][0], ',', q[2][1]);
source_table := array((1, 2), (3, 4), (2, 1));
setGlobalCache("PB_TEST_GC_SELECT", source_table);
getGlobalCache("PB_TEST_GC_SELECT", cached_table);
query_result := select * from cached_table order by [0] desc end;
writeLn(dataType(query_result));
writeLn(mrows(query_result));
writeLn(query_result[0][0], ',', query_result[0][1], ';', query_result[1][0], ',', query_result[1][1], ';', query_result[2][0], ',', query_result[2][1]);
```
结果说明:
@@ -355,8 +414,8 @@ end;
代码块身份:反例 / 不可照写
```text
r := Demo() with array("a": 11);
```tsl
cached_result := Demo() with array("a": 11);
function Demo();
begin
+126 -4
View File
@@ -212,6 +212,126 @@ matched := 1 in array(1, 2, 3);
row_matched := array(1, 2) sqlin array((1, 2), (3, 4));
```
### 点前缀比较算符产生逻辑数组
点前缀比较(`.=``.<>``.>``.>=``.<``.<=`)对数组/矩阵逐元素比较,返回真假值数组:
代码块身份:可直接照写示例
```tsl
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]);
```
代码块身份:输出片段
```text
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` 转换为下标数组,可用于取子集:
代码块身份:可直接照写示例
```tsl
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]);
```
代码块身份:输出片段
```text
逻辑数组: 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](22_matrix_deep_dive.md)
### 非完全矩阵与缺位当 0 处理
基础算符作用于非完全矩阵(行长度不一致或字符串键不对齐的数组)时,对应位置不存在或为 `nil` 时**默认当 0 处理**
代码块身份:可直接照写示例
```tsl
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"]);
```
代码块身份:输出片段
```text
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 = 3``result["B"] = 1 + 0 = 1``result["C"] = 1 + 2 = 3`
标量与矩阵的广播:
代码块身份:可直接照写示例
```tsl
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]);
```
代码块身份:输出片段
```text
(0,0): 11
(0,1): 12
(1,0): 13
(1,1): 14
```
说明:
- 标量 `+``-``*``/` 等作用于矩阵时,会广播到每个元素
- `matrix_value + 10` 每个元素都加 10
- 这些是逐元素运算(element-wise),区别于矩阵乘法 `:*`,见 [22_matrix_deep_dive.md](22_matrix_deep_dive.md)
## 本页不生成的范围
- 专门的结果集过滤函数
@@ -226,10 +346,12 @@ row_matched := array(1, 2) sqlin array((1, 2), (3, 4));
- 不要把矩阵链式比较 `::...` 和标量链式比较混写成同一种语法。
- 不要把 `in``sqlin` 当成同一个概念。
- 不要期待 `union2` 保留重复行。
- 不要用集合运算去做保留原始重复记录”的过滤任务。
- 不要把二维结果集默认当成按元素逐个比较”的集合运算。
- 左侧数组要表达这些值是否都属于右侧集合”时,用 `in`
- 左侧数组要表达这一整行是否存在于右侧结果集”时,用 `sqlin`
- 不要用集合运算去做保留原始重复记录”的过滤任务。
- 不要把二维结果集默认当成按元素逐个比较”的集合运算。
- 左侧数组要表达这些值是否都属于右侧集合”时,用 `in`
- 左侧数组要表达这一整行是否存在于右侧结果集”时,用 `sqlin`
- `minus` 表达集合差集;如果任务要求保留左侧原始重复次数,改走 [13_resultset_and_filters.md](13_resultset_and_filters.md) 的过滤规则。
- 不要在本页发明结果集过滤、TS-SQL 查询、写回语法或更大矩阵函数族。
- 不要把普通 `array(...)` 自动升级成 `FMArray`;只有任务明确命中时才进入 [23_fmarray.md](23_fmarray.md)。
- 不要把点前缀比较 `.>` 和矩阵链式比较 `::>` 混用;`.>` 返回逻辑数组,`::>` 是链式比较。
- 不要以为非完全矩阵缺位会报错;默认当 `0` 处理。
+1 -1
View File
@@ -18,7 +18,7 @@
2. 字段访问优先照本页明确的字符串键或结果集字段形态写。
3. 复杂查询需求优先跳转到 [14_ts_sql.md](14_ts_sql.md),不要把查询语法硬塞进过滤函数。
4. `in` / `sqlin` / `union2` / `intersect` / `minus` / `outersect` 这类去重型集合关系跳转到 [12_matrix_and_collections.md](12_matrix_and_collections.md)。
5. 金融数据筛选要先确认数据来源;函数事实见 [../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md),项目字段和业务上下文回项目实际接口。
5. 金融数据筛选要先确认数据来源;函数事实见 [../codegen/dotnet/datawarehouse/](../codegen/dotnet/datawarehouse/),项目字段和业务上下文回项目实际接口。
6. 没有对应代码块时不要发明结果集/过滤写法。
## 核心规则
+597 -19
View File
@@ -6,20 +6,22 @@
是否含不可照写反例:是
遇到不确定时:先按本页候选页继续判断;[13_resultset_and_filters.md](13_resultset_and_filters.md)、[12_matrix_and_collections.md](12_matrix_and_collections.md)、[23_fmarray.md](23_fmarray.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
这一篇是 TS-SQL 的唯一语法入口:内存数组查询、返回形态、字段访问、`where` / `group by` / `order by`、一维数组查询、多表 `join``thisGroup``thisRowIndex``refMaxOf` / `refMinOf` 都在这里收拢。
这一篇是 TS-SQL 的唯一语法入口:内存数组查询、返回形态、字段访问、`where` / `group by` / `order by`、一维数组查询、多表 `join`(含 `left join`)、`insert` / `update` / `delete` 写回`thisGroup``thisRowIndex``refMaxOf` / `refMinOf` 都在这里收拢。
## 本篇职责
回答写 TS-SQL 查询时,怎样从最小 `select ... from ... end` 骨架开始,逐步处理筛选、分组、排序、多表联接、组内子查询极值引用”。
回答写 TS-SQL 查询和写回时,怎样从最小 `select ... from ... end` 骨架开始,逐步处理筛选、分组、排序、多表联接(含 LEFT JOIN、组内子查询极值引用,以及如何用 `insert`/`update`/`delete` 修改内存数组”。
## 智能体 TS-SQL 判断流程
1. 先判断要写 `select``sselect``vselect``mselect`还是 `join` / `thisGroup` / 极值引用
1. 先判断要写查询(`select`/`sselect`/`vselect`/`mselect`还是写回(`insert`/`update`/`delete`
2. 内存数组查询优先从 `select ... from source_rows end` 最小骨架起手。
3. 二维结果集字段访问用 `["字段名"]`;多表查询字段访问用 `[表序号].["字段名"]`
4. 在一维数组上做 TS-SQL 时,优先使用 `thisRow``thisRowIndex`
5. 只想按已有结果集保留/排除行时跳到 [13_resultset_and_filters.md](13_resultset_and_filters.md);要做去重型集合关系时跳到 [12_matrix_and_collections.md](12_matrix_and_collections.md);要在 `FMArray` 上做查询或写回边界时跳到 [23_fmarray.md](23_fmarray.md)
6. 没有对应代码块时不要发明 TS-SQL 写法
5. 联接选 `join` / `left join` / `right join` / `full join` / `cross join` / 逗号联接;等值联接可用 `with(... on ...)` 优化
6. 聚集统一形态 `(Expr, Cond, N, MovingFirst, CacheId)`:条件聚集传 `Cond`,移动聚集传 `N`;分组后筛选用 `having`;自定义聚集用 `aggof`
7. 只想按已有结果集保留/排除行时跳到 [13_resultset_and_filters.md](13_resultset_and_filters.md);要做去重型集合关系时跳到 [12_matrix_and_collections.md](12_matrix_and_collections.md);要在 `FMArray` 上做查询或写回边界时跳到 [23_fmarray.md](23_fmarray.md)。
8. 没有对应代码块时不要发明 TS-SQL 写法。
## 核心规则
@@ -29,11 +31,17 @@
- 在内存二维结果集上,文档字段访问写法是 `["字段名"]`
- 在一维数组上做 TS-SQL 时,优先使用 `thisRow``thisRowIndex`
- `select` 返回二维结果,`sselect` 返回一维结果,`vselect` 返回单值,`mselect` 返回 `Matrix`
- `where``group by``order by` 可以直接接在 `from` 后面继续使用。
- 多表 `join` 时,字段访问应写成 `[表序号].["字段名"]`
- `where``group by``order by` 可以直接接在 `from` 后面继续使用`order by` 支持 `asc`/`desc` 与多列逗号分隔
- 分组后按聚集条件筛选用 `having``where` 不能用聚集);`having` 里用 `countof([字段])``countof(1)`,不要用 `countof(*)`
- 多表 `join` 时,字段访问应写成 `[表序号].["字段名"]``on` 可用 `and` 写多条件;`[表序号].*` 取整表列。
- 联接类型:`left join` 保留左表、`right join` 保留右表、`full join` 保留双方、`cross join` 笛卡尔积、逗号联接等价于 `cross join`;不匹配处用 `nil` 填充。
- `select` 列表支持 `distinct` 去重、`as 别名``as nil`(参与计算但不返回)、`起始列 to 结束列` 字段区间、`selectopt(位选项)``drange(区间/M of N)`
- 聚集函数统一形态 `Func(Expr[, Cond[, N[, MovingFirst[, CacheId]]]])`:条件聚集、移动聚集、多字段聚集、`refof(Expr, N)` 引用相对行;`aggof('名', Expr)` 调用自定义聚集回调。
- `thisGroup` 不是普通值,而是分组后的子结果集;要通过子 `select` / `vselect``from thisGroup` 来访问。
- `thisRowIndex``order by` 之后仍可返回原始行位置,而不是排序后的序号
- `thisRowIndex``order by` 之后仍可返回原始行位置`thisOrder` 返回排序后的自然排名
- `refMaxOf(...)``refMinOf(...)` 可与 `maxOf(...)` / `minOf(...)` 配合,取极值所在行的另一列值。
- `[@字段]` 返回该字段的数据类型字符串。
- 写回语句 `insert` / `update` / `delete` 直接修改原内存数组,不返回新数组。
- 访问金融表、时间序列或业务数据源时,语法和业务语义要分开看;本页只讲语言层查询骨架。
## 可直接照写示例
@@ -218,8 +226,8 @@ source_rows := array(
("A": 2, "B": 1, "Name": "y"),
("A": 1, "B": 2, "Name": "z")
);
group_result := select ["A"], maxb := maxOf(["B"]) as "MaxB",
vselect ["Name"] from thisGroup where ["B"] = maxb end as "TopName"
group_result := select ["A"], max_b := maxOf(["B"]) as "MaxB",
vselect ["Name"] from thisGroup where ["B"] = max_b end as "TopName"
from source_rows
group by ["A"]
order by ["A"]
@@ -270,17 +278,576 @@ min_ref_result := select minOf([0]) as "MinA", refMinOf([1]) as "RefB" from sour
- `min_ref_result` 只有一行,结果是 `(2,20)`
- 说明 `refMaxOf([1])` 取到了 `[0]` 最大值所在行的 `[1]``refMinOf([1])` 取到了 `[0]` 最小值所在行的 `[1]`
### `LEFT JOIN` 多表联接
`left join` 保留左表所有行,右表不匹配时用 `nil` 填充:
代码块身份:可直接照写示例
```tsl
left_rows := array(("k": 1, "va": "a1"), ("k": 2, "va": "a2"));
right_rows := array(("k": 1, "vb": "b1"), ("k": 3, "vb": "b3"));
result := select [1].["va"], [2].["vb"]
from left_rows left join right_rows
on [1].["k"] = [2].["k"]
end;
writeLn("行数:", mrows(result));
writeLn("(0,0):", result[0]["va"]);
writeLn("(0,1):", result[0]["vb"]);
writeLn("(1,0):", result[1]["va"]);
writeLn("(1,1):", result[1]["vb"]);
```
代码块身份:输出片段
```text
行数: 2
(0,0): a1
(0,1): b1
(1,0): a2
(1,1): nil
```
说明:
- `left join` 保留左表所有行(`k=1``k=2`
- 右表 `k=2` 不存在,对应列用 `nil` 填充
- `on` 子句指定联接条件,用 `[1].["k"] = [2].["k"]` 匹配键
- 其他联接类型:`right join`(保留右表)、`full join`(保留双方)、`cross join`(笛卡尔积)
### JOIN 家族其余形态
`right join` / `full join` 的不匹配行同样用 `nil` 填充:
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
b := array(("id": "01", "math": 70), ("id": "05", "math": 50));
right_result := select [1].["id"], [2].["math"] from a right join b on [1].["id"] = [2].["id"] end;
full_result := select [1].["id"], [2].["math"] from a full join b on [1].["id"] = [2].["id"] end;
writeLn("right行数:", mrows(right_result));
writeLn("full行数:", mrows(full_result));
```
代码块身份:输出片段
```text
right行数: 2
full行数: 3
```
说明:
- `right join` 保留右表所有行,左表无匹配的记录里,取自左表的字段为 `nil`(上例右表 `id=05` 那行 `[1].["id"]``nil`
- `full join` 保留双方所有行,任一侧无匹配的字段用 `nil` 填充(上例既有左表独占的 `02`,也有右表独占的 `05`
`cross join` 是笛卡尔积,不带 `on`;等价的逗号联接可带 `where`
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
b := array(("id": "01", "math": 70), ("id": "05", "math": 50));
cross_result := select [1].["id"], [2].["id"] from a cross join b end;
comma_result := select [1].["id"] from a, b where [1].["id"] = [2].["id"] end;
writeLn("cross行数:", mrows(cross_result));
writeLn("comma行数:", mrows(comma_result));
```
代码块身份:输出片段
```text
cross行数: 4
comma行数: 1
```
说明:
- `cross join` 不写 `on`,产生左右两表的全组合(2 × 2 = 4 行)
- `from A, B where ...` 是等价的逗号联接写法,用 `where` 表达匹配条件
`with(... on ...)` 是优化联接:把 `on` 的 N×M 匹配复杂度降到 N+M,多个等值约束用逗号分隔:
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "03", "en": 85));
b := array(("id": "01", "math": 70), ("id": "03", "math": 60), ("id": "05", "math": 50));
result := select [1].["id"], [2].["math"] from a join b with([1].["id"] on [2].["id"]) end;
writeLn("行数:", mrows(result));
```
代码块身份:输出片段
```text
行数: 2
```
说明:
- `join B with(左表达式组 on 右表达式组)` 只支持等值约束,靠哈希把复杂度降到 N+M
- 多个等值约束写成 `with([1].["id"], [1].["cls"] on [2].["id"], [2].["cls"])`
- 需要非等值条件(`>``<`)时回到普通 `join ... on`
`on` 支持多条件 `and``[表序号].*` 取整表所有列:
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "03", "en": 85));
b := array(("id": "01", "math": 70), ("id": "03", "math": 95));
result := select [1].* from a join b on [1].["id"] = [2].["id"] and [1].["en"] > [2].["math"] end;
writeLn("行数:", mrows(result));
writeLn("(0,0):", result[0]["id"]);
```
代码块身份:输出片段
```text
行数: 1
(0,0): 01
```
说明:
- `on` 后面可以用 `and` 串联多个条件
- `[1].*` 返回第一个表的所有列;三表以上时链式写 `join C on ...`
### `INSERT` 写回
`insert into` 向内存数组插入新行:
代码块身份:可直接照写示例
```tsl
a := array(("id": "x"));
insert into a insertfields(["id"]) values("y");
writeLn("行数:", mrows(a));
writeLn("(0,0):", a[0]["id"]);
writeLn("(1,0):", a[1]["id"]);
```
代码块身份:输出片段
```text
行数: 2
(0,0): x
(1,0): y
```
说明:
- `insert into 数组变量` 直接修改原数组
- `insertfields([字段列表])` 指定要插入的字段
- `values(...)` 提供对应值,可以写多组 `values``values` 实现批量插入
批量插入可以直接跟一个同结构数组,`insertfields` 也支持一次给多字段赋值:
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "cls": "A"));
rows := array(("id": "09", "cls": "C"));
insert into a rows;
insert into a insertfields(["id"], ["cls"]) values("07", "B");
writeLn("行数:", mrows(a));
writeLn(a[1]["id"], a[1]["cls"]);
writeLn(a[2]["id"], a[2]["cls"]);
```
代码块身份:输出片段
```text
行数: 3
09C
07B
```
说明:
- `insert into a rows;` 把整个 `rows` 数组的行批量追加进 `a`
- `insertfields(["id"], ["cls"]) values("07", "B")` 一次插入多字段
### `UPDATE` 写回
`update` 修改符合条件的行:
代码块身份:可直接照写示例
```tsl
a := array(("id": "x", "v": 1), ("id": "y", "v": 2));
update a set ["v"] = 99 where ["id"] = "y" end;
writeLn(a[0]["v"]);
writeLn(a[1]["v"]);
```
代码块身份:输出片段
```text
1
99
```
说明:
- `update 数组变量 set [字段] = 新值` 直接修改原数组
- `where` 子句筛选要更新的行
- 可以同时更新多个字段:`set ["v1"] = 10, ["v2"] = 20`
`set` 一个不存在的列会自动新增该列;`set thisrow = ...` 可以整行替换(一维数组上尤其常用):
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
update a set ["total"] = ["en"] + 1 end;
r := array(1, 2, 3);
update r set thisrow = thisrow * 10 end;
writeLn(a[0]["total"]);
writeLn(r[0], r[1], r[2]);
```
代码块身份:输出片段
```text
91
102030
```
说明:
- `set ["total"] = ...``total` 原本不存在,执行后自动作为新列加到每一行
- `set thisrow = thisrow * 10` 用当前行整体做表达式并写回;一维数组上 `thisrow` 就是元素本身
### `DELETE` 写回
`delete` 删除符合条件的行:
代码块身份:可直接照写示例
```tsl
a := array(("id": "x"), ("id": "y"));
delete from a where ["id"] = "x";
writeLn("行数:", mrows(a));
writeLn("(0,0):", a[0]["id"]);
```
代码块身份:输出片段
```text
行数: 1
(0,0): y
```
说明:
- `delete from 数组变量 where 条件` 删除符合条件的行
- 直接修改原数组
- 省略 `where` 会删除所有行
`deleteopt(Option)` 控制删除后是否重排下标:
代码块身份:可直接照写示例
```tsl
a := array(("cls": "A"), ("cls": "A"), ("cls": "B"), ("cls": "B"));
delete deleteopt(1) from a where ["cls"] = "A";
writeLn(mrows(a));
```
代码块身份:输出片段
```text
2
```
说明:
- `deleteopt(0)`(默认)删除后重排剩余行下标
- `deleteopt(1)` 删除后保留原始下标,不重排
### `distinct` 结果集去重
`select distinct` 对结果集去重;聚集函数内也可用 `distinct` 前缀:
代码块身份:可直接照写示例
```tsl
a := array(("cls": "A", "en": 90), ("cls": "A", "en": 80), ("cls": "B", "en": 85));
distinct_rows := select distinct ["cls"] from a end;
distinct_sum := vselect sumof(distinct ["en"]) from a end;
writeLn(mrows(distinct_rows));
writeLn(distinct_sum);
```
代码块身份:输出片段
```text
2
255
```
说明:
- `select distinct [字段]` 折叠重复行
- `sumof(distinct [字段])` 只对不同值求和:`90 + 80 + 85 = 255`
### `as` 别名、`as nil` 与字段区间
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "cls": "A", "en": 90), ("id": "02", "cls": "B", "en": 80));
alias_rows := select ["en"] as "score" from a end;
nil_rows := select ["id"], ["en"] * 2 as nil from a end;
range_rows := select 0 to 1 from a end;
writeLn(alias_rows[0]["score"]);
writeLn(mcols(nil_rows));
writeLn(mcols(range_rows));
```
代码块身份:输出片段
```text
90
1
2
```
说明:
- `字段 as "别名"` 给结果列改名
- `表达式 as nil` 让该列只参与临时计算,不出现在结果集里(上例 `nil_rows` 只剩 `id` 一列)
- `StartIndex to EndIndex` 在选择列表里取列区间;`0 to 1` 返回第 0、1 两列
### `drange` 取行区间
代码块身份:可直接照写示例
```tsl
a := array(("id": "01"), ("id": "02"), ("id": "03"), ("id": "04"));
head_rows := select drange(0 to 1) * from a end;
tail_rows := select drange(-2 to -1) * from a end;
part_rows := select drange(1 of 2) * from a end;
writeLn(mrows(head_rows));
writeLn(tail_rows[0]["id"]);
writeLn(mrows(part_rows));
```
代码块身份:输出片段
```text
2
03
2
```
说明:
- `drange(begin to end)` 取行区间;负数从末尾计(`-1` 是最后一行)
- `drange(M of N)` 把结果集等分成 N 份,取第 M 份
### `selectopt` 位选项
代码块身份:可直接照写示例
```tsl
a := array(("en": 90), ("en": 80), ("en": 85), ("en": 85));
opt_rows := select selectopt(2) ["en"] from a end;
writeLn(dataType(opt_rows));
writeLn(opt_rows[0]);
```
代码块身份:输出片段
```text
4
90
```
说明:
- `selectopt(N)` 用位组合改变返回形态;`selectopt(2)``select` 返回一维数组(不再是二维结果集)
- 常用位:`1`=单值、`2`=一维、`4`=Matrix、`16`=多字段聚集保留原名、`64`=MovingFirst 变换
### 条件聚集、移动聚集与 `refof`
聚集函数统一支持 `(Expr, BoolConditionExp, N, MovingFirst, CacheId)` 形态:
代码块身份:可直接照写示例
```tsl
a := array(("en": 90), ("en": 80), ("en": 85), ("en": 85));
cond_sum := vselect sumof(["en"], ["en"] >= 85) from a end;
moving := select avgof(["en"], true, 2, true) from a end;
ref_prev := select ["en"], refof(["en"], 1) from a end;
writeLn(cond_sum);
writeLn(moving[2]["Expr1"]);
writeLn(ref_prev[1]["Expr1"]);
```
代码块身份:输出片段
```text
260
82.5
90
```
说明:
- 条件聚集:`sumof(表达式, 布尔条件)` 只统计条件为真的行(`90 + 85 + 85 = 260`
- 移动聚集:`avgof(表达式, 条件, N, MovingFirst)` 取当前行往前 N 条的滑动统计
- `refof(表达式, N)` 引用前 N 行的值(`N` 为负则往后);首行无前值时返回 `0`
### `group by ... having`
`having` 用聚集条件筛选分组(`where` 不能用聚集):
代码块身份:可直接照写示例
```tsl
a := array(("cls": "A", "en": 90), ("cls": "A", "en": 80), ("cls": "B", "en": 85));
having_rows := select ["cls"] from a group by ["cls"] having countof(["cls"]) > 1 end;
writeLn(mrows(having_rows));
writeLn(having_rows[0]["cls"]);
```
代码块身份:输出片段
```text
1
A
```
说明:
- `having 聚集条件` 在分组后筛选(上例只保留成员数大于 1 的 `A` 组)
- `having` 里的计数用 `countof([字段])``countof(1)`
代码块身份:反例 / 不可照写
```text
having_rows := select ["cls"] from a group by ["cls"] having countof(*) > 1 end;
```
`countof(*)` 这种带 `*` 的写法不成立,会报 `CountOf ( not found`。计数改用 `countof([字段])``countof(1)`
### `thisOrder` 与多列 `order by`
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "02", "en": 80), ("id": "03", "en": 85), ("id": "04", "en": 85));
order_rows := select ["id"], thisOrder as "ord" from a order by ["en"] end;
desc_rows := select ["id"] from a order by ["en"] desc end;
writeLn(order_rows[0]["id"], ",", order_rows[0]["ord"]);
writeLn(desc_rows[0]["id"]);
```
代码块身份:输出片段
```text
02,1
01
```
说明:
- `thisOrder` 返回排序后的自然排名(从 1 起,值相同则同名次,名次可不连续)
- `order by [字段] desc` 降序;多列排序用逗号分隔:`order by ["cls"] asc, ["en"] desc`
- `thisOrder``thisRowIndex` 不同:后者返回排序前的原始行位置
### `refsof` 引用上级结果集
在嵌套子查询里,`refsof(Exp, UpLevel)` 用上 N 级结果集计算 `Exp`
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
r := select ["id"], (vselect refsof(["en"], 1) from a end) as "up" from a end;
writeLn(r[0]["id"], ",", r[0]["up"]);
writeLn(r[1]["id"], ",", r[1]["up"]);
```
代码块身份:输出片段
```text
01,90
02,80
```
说明:
- `refsof(表达式, 1)` 在内层子查询里引用上一级(外层)结果集当前行的值
- 数字越大引用越靠外层;脱离嵌套单独使用会报 `no result set for reference`
### `[@Field]` 取字段类型
代码块身份:可直接照写示例
```tsl
a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
t := select ["id"], [@"en"] from a end;
writeLn(t[0]["Expr1"]);
```
代码块身份:输出片段
```text
integer
```
说明:
- `[@字段名]` 返回该字段的数据类型名(字符串),可用于 select 列表与 update set
### `aggof` 自定义聚集扩展
> 代码块身份:仅服务端可执行示例
> 本地 `TSL.exe` 会报 `AggOf Init Error`;下例在服务端(pyTSL)验证通过。
`aggof('名称', 表达式)` 调用一个自定义回调函数做聚集:
代码块身份:仅服务端可执行示例
```tsl
Table1 := array(("C": 1), ("C": 2), ("C": 3), ("C": 4), ("C": 5));
return vselect aggof('AggSumSample', ['C']) from Table1 end;
function AggSumSample(Flag, Value);
begin
if Flag = 0 then
begin
sysParams['SumSample'] := 0;
return true;
end
else if Flag = 1 then
begin
sysParams['SumSample'] := sysParams['SumSample'] + Value;
return true;
end
else
return sysParams['SumSample'];
end;
```
说明(上例返回 `15`):
- 回调签名固定为 `function Name(Flag, Value)`
- `Flag=0` 初始化(`Value` 为真表示 `distinct`),返回 `true`/`false` 表示成败
- `Flag=1` 每行数据,`Value` 是当前行表达式的值,返回 `true`/`false`
- `Flag=2` 结束,返回最终聚集结果
- 状态用 `sysParams[...]` 缓存,不要用 `static`
## 本页不生成的范围
- `with on`
- `left join` / `right join` / `full join`
- `RefsOf`
- `insert` / `update` / `delete`
- `TSQLInsert` / `TSQLSetValue` / `TSQLBatchInsert`
- `TSQLEdit` / `TSQLPost` / `TSQLFinal`
- 面向 SQL 表、业务表或时间序列的数据查询与写回
- `TSQLInsert` / `TSQLSetValue` / `TSQLBatchInsert` / `TSQLEdit` / `TSQLPost` / `TSQLFinal` 对象被 TS-SQL 查询的回调机制
- 面向 SQL 表、业务表或时间序列的数据查询与写回(`marketTable` / `infoTable` / `tradeTable` / `sqlTable` / `hugeSqlTable` 等数据源)
这些内容不作为本页可生成事实;业务数据源先查 [../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md)、[../modules/pytsl_api.md](../modules/pytsl_api.md) 或项目实际接口。
这些内容不作为本页可生成事实;业务数据源先查 [../codegen/dotnet/datawarehouse/](../codegen/dotnet/datawarehouse/)、[../modules/pytsl_api.md](../modules/pytsl_api.md) 或项目实际接口。
## 默认生成模板
@@ -301,7 +868,18 @@ query_result := select * from source_rows end;
- 处理一维数组时直接把 `[0]` 当成稳定列访问。
- 把 `thisGroup` 当成普通字段或普通变量。
- 把排序后的 `thisRowIndex` 误当成排序序号。
- `with on`、写回接口和时间序列缓存写进默认模板
- `left join` 时省略 `on` 子句或不用 `[表序号].["字段"]` 形式
- 在 `insert` 时漏掉 `insertfields` 或字段数与值数不匹配。
- 期望 `update`/`delete` 返回新数组;它们直接修改原数组。
- 用 `countof(*)` 数行数;`*` 星号形式不被支持,改用 `countof([字段])``countof(1)`
代码块身份:反例 / 不可照写
```text
n := vselect countof(*) from source_rows end;
```
`countof(*)` 会报 `CountOf ( not found`。数行数改用 `countof([字段])``countof(1)`
代码块身份:反例 / 不可照写
+21 -21
View File
@@ -28,7 +28,7 @@
- `debugRunEnv(0)``debugRunEnv(1)` 可直接调用;它们面向调试客户端的副作用,不作为本页输出事实。
- `debugRunEnvDo Func(...)` 可直接写,并且会返回被调用函数的结果。
- `mtic` 会生成一个计时起点;`mtoc``mtoc(tick)` 都会返回秒数。
- `setProfiler(...)``getProfilerInfo(...)` 的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md);本页只保留性能分析器行为示例。
- `setProfiler(...)``getProfilerInfo(...)` 的参数规格见 [../codegen/builtin/system.md](../codegen/builtin/system.md);本页只保留性能分析器行为示例。
- `setProfiler(7)` 配合 `getProfilerInfo(1)`,可以在不弹窗的情况下拿到性能分析器信息。
- `__line__` 会返回所在代码行号。
- `__stack_frame` 会返回调用栈帧数组;最小 `toStn(...)` 观察结果里,每一项是 `(line, "function")` 这一类二元组。
@@ -108,9 +108,9 @@ writeLn("before");
a := Inner(3);
writeLn("after");
function Inner(bb);
function Inner(value);
begin
debugReturn bb;
debugReturn value;
end;
```
@@ -144,8 +144,8 @@ writeLn(1);
代码块身份:可直接照写示例
```tsl
r := debugRunEnvDo Demo(2);
writeLn(r);
result := debugRunEnvDo Demo(2);
writeLn(result);
function Demo(x);
begin
@@ -165,17 +165,17 @@ end;
代码块身份:可直接照写示例
```tsl
t1 := mtic;
s := 0;
tick1 := mtic;
sum := 0;
for i := 0 to 9999 do
s := s + i;
te1 := mtoc(t1);
t2 := mtic;
sum := sum + i;
elapsed1 := mtoc(tick1);
tick2 := mtic;
for j := 0 to 9999 do
s := s + j;
te2 := mtoc;
writeLn(te1 >= 0);
writeLn(te2 >= 0);
sum := sum + j;
elapsed2 := mtoc;
writeLn(elapsed1 >= 0);
writeLn(elapsed2 >= 0);
```
结果说明:
@@ -189,9 +189,9 @@ writeLn(te2 >= 0);
```tsl
setProfiler(7);
a := 99;
b := intToStr(a);
c := rand(10, 1);
value := 99;
str := intToStr(value);
random := rand(10, 1);
info := getProfilerInfo(1);
writeLn(ifArray(info));
writeLn(length(info) > 0);
@@ -210,8 +210,8 @@ writeLn(length(info) > 0);
代码块身份:可直接照写示例
```tsl
a := __line__;
writeLn(a);
line_number := __line__;
writeLn(line_number);
```
结果说明:
@@ -224,8 +224,8 @@ writeLn(a);
代码块身份:可直接照写示例
```tsl
s := Outer();
writeLn(toStn(s));
stack := Outer();
writeLn(toStn(stack));
function Inner();
begin
@@ -170,6 +170,30 @@ writeLn(1);
- 输出 `1`
- 说明未命中的条件编译分支不会参与脚本编译
### `{$CompileOption}` 编译选项
`{$CompileOption}` 用于设置编译期开关,改变编译器的默认行为:
代码块身份:可直接照写示例
```tsl
{$CompileOption optimize=1}
echo 1 + 1;
```
代码块身份:输出片段
```text
2
```
说明:
- `{$CompileOption optimize=1}` 开启优化
- 编译选项从出现位置开始生效,直到源文件结束或被其他选项覆盖
- 常见选项包括 `optimize``buffermode``DebugInfo`
- 编译选项细节以项目工具链和实际编译命令为准。
### 参数默认传递开关
`{$varByRef-}``{$varByRef+}`
@@ -190,23 +214,23 @@ r := 1;
TouchRestored(r);
writeLn(r);
function TouchDefault(a);
function TouchDefault(value);
begin
a := 9;
value := 9;
end;
{$varByRef-}
function TouchValue(a);
function TouchValue(value);
begin
a := 8;
value := 8;
end;
function TouchForcedVar(var a);
function TouchForcedVar(var value);
begin
a := 7;
value := 7;
end;
{$varByRef+}
function TouchRestored(a);
function TouchRestored(value);
begin
a := 6;
value := 6;
end;
```
+45 -4
View File
@@ -45,6 +45,7 @@
- `dataType(z)` 对复数返回 `41``ifComplex(z)` 对复数返回 `1`
- `real``imag``conj``abs` 都可用于复数。
- 实数 `x` 与复数 `x + 0j` 的相等比较结果为真。
- 复数支持四则运算 `+` / `-` / `*` / `/`,可与实数混合运算;矩阵乘用 `:*`
- `complex(array(...), imag)` 会返回 `Array``complex(fmarray..., imag)` 会返回 `FMArray`,并且其单元格类型是 `41`
## 可直接照写示例
@@ -138,14 +139,14 @@ writeLn(nan = nan);
```tsl
writeLn(a);
h := new Holder();
writeLn(h.value = nil);
holder := new Holder();
writeLn(holder.Value = nil);
arr := array();
writeLn(arr[0] = nil);
type Holder = class
public
value;
Value;
end;
```
@@ -153,7 +154,7 @@ end;
- 依次输出 `0``1``1`
- 说明未初始化普通变量默认是整数 `0`
- 说明类成员默认值是 `nil`
- 说明类成员默认值是 `nil``holder.Value`
- 也说明空数组读取尚未赋值的下标时,结果是 `nil`
`nil` 的显式判定与加法边界:
@@ -304,6 +305,46 @@ writeLn(f[0], ',', f[1], ',', f[2]);
- 上述复数 `FMArray` 的单元格类型 `dataType(f, 1)``41`
- 该 `FMArray` 长度是 `3`,三个元素依次是 `1+5.5j``2+5.5j``3+5.5j`
复数四则运算:
代码块身份:可直接照写示例
```tsl
z1 := 4 + 3j;
z2 := 5 + 12j;
writeLn(z1 + z2);
writeLn(z2 - z1);
writeLn(z1 * z2);
writeLn(z2 / z1);
```
结果说明:
- `z1 + z2``9.0+15.0j`
- `z2 - z1``1.0+9.0j`
- `z1 * z2``-16.0+63.0j`
- `z2 / z1``2.24+1.32j`
- 复数参与运算后实部虚部按浮点显示(带 `.0`
- 复数在四则运算上和实数写法一致,也支持与实数混合运算
复数矩阵乘 `:*`
代码块身份:可直接照写示例
```tsl
a := array((-1 + 1j), (-3 + 1j));
b := array((2 + 2j, 1 + 3j));
writeLn(tostn(a :* b));
```
结果说明:
- 复数矩阵同样用 `:*` 做矩阵乘(区别于逐单元乘 `*`),矩阵乘除乘方算符体系见 [22_matrix_deep_dive.md](22_matrix_deep_dive.md)
- `a``2 x 1``b``1 x 2``a :* b``2 x 2` 结果矩阵
- 结果是 `array((-4.0+0.0j,-4.0-2.0j),(-8.0-4.0j,-6.0-8.0j))`
> 版本边界:复数相关功能只在新一代客户端与新一代服务器支持;本节示例在支持复数的运行环境(如新一代服务端)验证。虚数单位固定用 `j`,不是 `i`
复数最短骨架:
代码块身份:可直接照写示例
@@ -113,10 +113,10 @@ function TickCdecl(): int64; cdecl; external "kernel32.dll" name "GetTickCount64
代码块身份:可直接照写示例
```tsl
t1 := Tick64();
tick_before := Tick64();
SleepMs(20);
t2 := Tick64();
writeLn(t2 >= t1);
tick_after := Tick64();
writeLn(tick_after >= tick_before);
function Tick64(): int64; stdcall; external "kernel32.dll" name "GetTickCount64";
procedure SleepMs(ms: integer); stdcall; external "kernel32.dll" name "Sleep";
@@ -132,15 +132,15 @@ procedure SleepMs(ms: integer); stdcall; external "kernel32.dll" name "Sleep";
代码块身份:可直接照写示例
```tsl
h := LoadLibraryA("kernel32.dll");
fp := GetProcAddress(h, "GetTickCount64");
f := function(): int64; stdcall; external fp;
writeLn(h <> nil);
writeLn(fp <> nil);
writeLn(##f() > 0);
module_handle := LoadLibraryA("kernel32.dll");
func_ptr := GetProcAddress(module_handle, "GetTickCount64");
wrapped_func := function(): int64; stdcall; external func_ptr;
writeLn(module_handle <> nil);
writeLn(func_ptr <> nil);
writeLn(##wrapped_func() > 0);
function LoadLibraryA(s: string): pointer; stdcall; external "kernel32.dll" name "LoadLibraryA";
function GetProcAddress(hModule: pointer; lpProcName: string): pointer; stdcall; external "kernel32.dll" name "GetProcAddress";
function LoadLibraryA(lib_name: string): pointer; stdcall; external "kernel32.dll" name "LoadLibraryA";
function GetProcAddress(module_handle: pointer; proc_name: string): pointer; stdcall; external "kernel32.dll" name "GetProcAddress";
```
结果说明:
@@ -155,17 +155,17 @@ function GetProcAddress(hModule: pointer; lpProcName: string): pointer; stdcall;
代码块身份:可直接照写示例
```tsl
d := new Demo();
writeLn(d.Run());
demo := new Demo();
writeLn(demo.Run());
type Demo = class
public
const kernel_dll = "kernel32.dll";
const kKernelDll = "kernel32.dll";
function Run();
begin
return TickConst() > 0;
end;
function TickConst(): int64; stdcall; external kernel_dll name "GetTickCount64";
function TickConst(): int64; stdcall; external kKernelDll name "GetTickCount64";
end;
```
@@ -189,10 +189,10 @@ function TickFromExpr(): int64; stdcall; external "kernel32"$"."$"dll" name "Get
代码块身份:可直接照写示例
```tsl
fp := makeInstance(thisFunction(Add), "cdecl", 0);
f := function(a: integer; b: integer): integer; external fp;
writeLn(fp <> nil);
writeLn(##f(3, 4));
func_ptr := makeInstance(thisFunction(Add), "cdecl", 0);
wrapped_func := function(a: integer; b: integer): integer; external func_ptr;
writeLn(func_ptr <> nil);
writeLn(##wrapped_func(3, 4));
function Add(a: integer; b: integer): integer;
begin
@@ -212,19 +212,19 @@ end;
```tsl
setGlobalCache("THREAD_TEST_KEY", 0);
fp := makeInstance(thisFunction(Worker), "cdecl", 1);
h := CreateThread(nil, nil, fp, nil, 0, tid);
writeLn(fp <> nil);
writeLn(h <> nil);
writeLn(WaitForSingleObject(h, 5000) >= 0);
getGlobalCache("THREAD_TEST_KEY", v);
writeLn(v);
CloseHandle(h);
worker_ptr := makeInstance(thisFunction(Worker), "cdecl", 1);
thread_handle := CreateThread(nil, nil, worker_ptr, nil, 0, thread_id);
writeLn(worker_ptr <> nil);
writeLn(thread_handle <> nil);
writeLn(WaitForSingleObject(thread_handle, 5000) >= 0);
getGlobalCache("THREAD_TEST_KEY", result_value);
writeLn(result_value);
CloseHandle(thread_handle);
function CreateThread(attr: pointer; size: pointer; addr: pointer; p: pointer; flag: Integer; var thread_id: Integer): pointer; stdcall; external "kernel32.dll" name "CreateThread";
function WaitForSingleObject(h: pointer; timeout: Integer): Integer; stdcall; external "kernel32.dll" name "WaitForSingleObject";
function CloseHandle(h: pointer): Integer; stdcall; external "kernel32.dll" name "CloseHandle";
function Worker(p: pointer): integer;
function WaitForSingleObject(handle: pointer; timeout: Integer): Integer; stdcall; external "kernel32.dll" name "WaitForSingleObject";
function CloseHandle(handle: pointer): Integer; stdcall; external "kernel32.dll" name "CloseHandle";
function Worker(param: pointer): integer;
begin
setGlobalCache("THREAD_TEST_KEY", 1);
return 1;
@@ -35,7 +35,7 @@
- 直接写 `DemoUnit.Member` 时,可以读到 `interface``implementation` 里的常量、变量。
- `findFunction("DemoUnit")` 拿到的是 `unit` 对象入口;本页只把它稳定暴露 `interface` 成员写成文档事实。
- `DemoUnit.var_name := value` 这种限定赋值不作为可写事实;如果要改 `unit` 状态,应导出函数或方法来改。
- `tslfilename()` 的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md);本页只保留它返回正在执行的 `.tsl` 主脚本完整路径这一行为事实。
- `tslfilename()` 的参数规格见 [../codegen/builtin/system.md](../codegen/builtin/system.md);本页只保留它返回正在执行的 `.tsl` 主脚本完整路径这一行为事实。
- `namespace "DemoNS";` 会选择 `Hello@DemoNS.tsf` 这类命名空间函数文件。
- `tsl.conf``[system] Namespace=...` 可以设置默认命名空间;脚本里的 `namespace "..."` 会覆盖配置值。
- 当全局 `Hello.tsf``Hello@StmtNS.tsf` 同时存在时,启用 `StmtNS` 后会优先命中命名空间版本。
@@ -18,7 +18,7 @@
2. 普通对象创建、普通方法调用、类声明和继承都回到 [08_objects_and_classes.md](08_objects_and_classes.md),不要把 `findClass(...)` / `createObject(...)` 当默认写法。
3. 确实命中反射时,入口优先照 `findClass``findFunction``thisFunction``findOverLoad` 等文档明确示例写。
4. 访问弱引用前先做 `checkWeakRef(...)` 判定,不要假设失效弱引用安全返回 `nil`
5. 类内 `weakRef;` / `autoRef;` 段落式写法属于反例,不要照写
5. 类内段落式 `weakRef` / `autoRef`(不带分号)是合法的成员弱引用开关;只有带分号的 `weakRef;` / `autoRef;` 才报 `invalid class definition`
6. 函数值调用边界回看函数页,避免把函数指针直接当普通函数调用。
7. 没有对应代码块时不要发明对象运行时/反射/弱引用写法。
@@ -48,7 +48,7 @@
- 对已经失效的弱引用直接调用 `weakref_get(w)` 会运行报错,不要把它当成“安全返回 nil”的接口。
- 类成员上的 `[weakRef] field;``[autoRef] field;` 可以通过。
- `[weakRef]` 成员不会阻止对象析构;强引用释放后,对象会正常销毁。
- 类内段落式 `weakRef;` / `autoRef;` 不作为可写事实,错误信息包含 `invalid class definition`
- 类内段落式 `weakRef` / `autoRef`(不带分号)像 `public` / `private` 一样切换后续成员的弱引用属性,可写;只有带分号的 `weakRef;` / `autoRef;` 才报 `invalid class definition`
## 可直接照写示例
@@ -84,12 +84,12 @@ end;
```tsl
classA := findClass("A");
obj := new A();
writeLn(classA.FucA());
writeLn(classA.FuncA());
writeLn(obj is classA);
type A = class
public
class function FucA();
class function FuncA();
begin
return "ClassA";
end;
@@ -110,18 +110,18 @@ end;
```tsl
objb := new ClassB();
obj := findClass("ClassA", objb);
writeLn(obj.Fuc());
writeLn(obj.Func());
type ClassA = class
public
function Fuc(); virtual;
function Func(); virtual;
begin
return "ClassA";
end;
end;
type ClassB = class(ClassA)
public
function Fuc(); override;
function Func(); override;
begin
return "ClassB";
end;
@@ -183,10 +183,10 @@ end;
代码块身份:可直接照写示例
```tsl
oa := new ca("abc");
oa := new Ca("abc");
writeLn(objectstate(oa));
type ca = class
type Ca = class
public
static sca;
function create(n);
@@ -208,20 +208,20 @@ end;
代码块身份:可直接照写示例
```tsl
o := new ca();
o := new Ca();
o.c := 3;
writeLn(o.c);
type ca = class
type Ca = class
public
value;
property c read getc write setc;
function setc(v);
property c read Getc write Setc;
function Setc(v);
begin
writeLn(tslassigning);
value := v;
end;
function getc();
function Getc();
begin
writeLn(tslassigning);
return value;
@@ -302,16 +302,16 @@ end;
代码块身份:可直接照写示例
```tsl
t := findOverLoad(2, "fun", new TestClass());
t := findOverLoad(2, "Fun", new TestClass());
writeLn(t.do(1, 2));
type TestClass = class
public
function fun(p1, p2); overload;
function Fun(p1, p2); overload;
begin
return p1 + p2;
end;
function fun(p1); overload;
function Fun(p1); overload;
begin
return p1 + 10;
end;
@@ -321,7 +321,7 @@ end;
结果说明:
- 输出 `3`
- 说明 `findOverLoad(2, "fun", obj)` 可以按参数个数拿到对应重载方法
- 说明 `findOverLoad(2, "Fun", obj)` 可以按参数个数拿到对应重载方法
### 函数信息、对象枚举与生命周期
@@ -356,14 +356,15 @@ end;
代码块身份:可直接照写示例
```tsl
objA := new TestClass01(100);
objB := new TestClass01(101);
objsInfo := tslObjects(1);
writeLn(length(objsInfo["TestClass01"]));
newObjA := objsInfo["TestClass01"][0, "obj"];
writeLn(newObjA is class(TestClass01));
writeLn(newObjA.add(1, 2));
objA := new TestClass01(100);
objB := new TestClass01(101);
type TestClass01 = class
public
value;
@@ -371,7 +372,7 @@ public
begin
value := _value;
end;
class function add(x, y);
class function Add(x, y);
begin
return x + y;
end;
@@ -556,7 +557,7 @@ end;
- 不要把 `objectstate(self)` 里的 `self` 泛化成普通成员访问都要加 `self` 前缀。
- 不要把弱引用能力的条件编译宏写成 `weakRef`
- 不要以为 `weakref_get(deadWeakRef)` 会像普通可空访问那样安全返回 `nil`
- 不要以为类内段落式 `weakRef;` / `autoRef;` 也能直接通过
- 不要给段落切换关键字加分号;`weakRef;` / `autoRef;`(带分号)会报 `invalid class definition`,段落式要写成不带分号的 `weakRef` / `autoRef`
- 不要以为 `[weakRef]` 成员会继续强持有对象。
代码块身份:反例 / 不可照写
@@ -573,4 +574,33 @@ public
end;
```
上面这种`weakRef;` / `autoRef;` 当作类内段落切换的写法不作为可写事实,会编译失败,错误信息包含 `invalid class definition`
上面这种给段落切换关键字**加分号**`weakRef;` / `autoRef;`)的写法会编译失败,错误信息包含 `invalid class definition`关键在分号:段落切换关键字要像 `public` / `private` 那样**不带分号**独占一行,带分号后被当成独立语句才报错。正确的段落式写法见下一段。
段落式 `weakRef` / `autoRef`(不带分号)像 `public` / `private` 一样切换后续成员的弱引用属性:
代码块身份:可直接照写示例
```tsl
o := new AutoWeakTest();
o.fa := 1;
o.fb := 2;
writeLn(o.fa, ',', o.fb);
type AutoWeakTest = class
public
fa;
weakRef
fOnClick;
fOnDblClick;
autoRef
fb;
end;
```
结果说明:
- 输出 `1,2`
- `weakRef`(不带分号)打开后续成员的自动弱引用开关,`fOnClick` / `fOnDblClick` 成为弱引用成员
- `autoRef`(不带分号)关闭开关,`fb` 恢复为强引用成员
- 段落切换只影响其后、下一个切换关键字之前的成员;`fa` 在初始(强引用)段
- 这与行内 `[weakRef] field;` 是两种可用写法:行内只作用于单个成员,段落式作用于整段
+12 -12
View File
@@ -46,13 +46,13 @@
代码块身份:可直接照写示例
```tsl
obj := new TStringList();
obj.CommaText := "A=aaa,B=bbb,C=222";
writeLn(obj[1]);
writeLn(obj["B"]);
writeLn(obj.Count);
obj.Add("D=444");
writeLn(obj.Count);
list := new TStringList();
list.CommaText := "A=aaa,B=bbb,C=222";
writeLn(list[1]);
writeLn(list["B"]);
writeLn(list.Count);
list.Add("D=444");
writeLn(list.Count);
```
结果说明:
@@ -65,11 +65,11 @@ writeLn(obj.Count);
代码块身份:可直接照写示例
```tsl
obj := new THashedStringList();
obj.Add("A=aaa");
obj.Add("B=bbb");
writeLn(obj.Count);
writeLn(obj["B"]);
list := new THashedStringList();
list.Add("A=aaa");
list.Add("B=bbb");
writeLn(list.Count);
writeLn(list["B"]);
```
结果说明:
+384 -4
View File
@@ -6,11 +6,11 @@
是否含不可照写反例:是
遇到不确定时:先按本页候选页继续判断;[12_matrix_and_collections.md](12_matrix_and_collections.md)、[23_fmarray.md](23_fmarray.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
这一篇只讲矩阵专用语法主干:矩阵初始化、数列构造、矩阵逆/广义逆、矩阵尺寸与索引、矩阵遍历、子矩阵和 `mfind` 查找。它和 [12_matrix_and_collections.md](12_matrix_and_collections.md) 的分工是:`12` 讲普通数组与集合关系,这一篇讲矩阵专用构造、遍历、子矩阵和矩阵查找接口。
这一篇只讲矩阵专用语法主干:矩阵初始化、数列构造、矩阵逆/广义逆、矩阵乘除乘方、矩阵转置、矩阵拼接、矩阵尺寸与索引、矩阵遍历、子矩阵和 `mfind` 查找。它和 [12_matrix_and_collections.md](12_matrix_and_collections.md) 的分工是:`12` 讲普通数组与集合关系,这一篇讲矩阵专用构造、运算、遍历、子矩阵和矩阵查找接口。
## 本篇职责
回答怎样直接构造全零矩阵、全一矩阵、随机矩阵、单位矩阵、空矩阵和数列数组,怎样写矩阵逆/广义逆,怎样拿到矩阵的行数、列数、行索引和列索引,怎样遍历矩阵、取/改子矩阵,以及怎样用 `mfind` 找到或替换符合条件的单元格”。
回答怎样直接构造全零矩阵、全一矩阵、随机矩阵、单位矩阵、空矩阵和数列数组,怎样写矩阵逆/广义逆,怎样进行矩阵乘除乘方,怎样转置矩阵,怎样拼接矩阵,怎样拿到矩阵的行数、列数、行索引和列索引,怎样遍历矩阵、取/改子矩阵,以及怎样用 `mfind` 找到或替换符合条件的单元格”。
## 智能体矩阵深水判断流程
@@ -26,7 +26,7 @@
## 核心规则
- 矩阵初始化函数的参数规格见 [../reference/catalog/math.md](../reference/catalog/math.md);本页只保留矩阵行为示例和返回形态边界。
- 矩阵初始化函数的参数规格见 [../codegen/builtin/math.md](../codegen/builtin/math.md);本页只保留矩阵行为示例和返回形态边界。
- `zeros(...)``ones(...)``rand(...)``nils(...)``eye(...)` 都可以直接用于矩阵初始化。
- `zeros(3)``ones(3)``nils(2)` 这类单参数写法可以直接生成一维结果。
- `zeros(2, 3)``rand(2, 3)` 这类双参数写法可以直接生成二维矩阵。
@@ -34,7 +34,7 @@
- `eye(3)` 生成的是 `3 x 3` 单位矩阵,不是一维数组。
- `->` 用来生成数列;默认步长是 `1`,也可以显式传入步长和索引数组。
- 在矩阵语境里,`!A` 是一元倒数运算符作用于矩阵的形态,用于矩阵逆/广义逆;非方阵输入可以返回行列数互换后的广义逆结果。
- `msize(...)``mrows(...)``mcols(...)` 的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md)。
- `msize(...)``mrows(...)``mcols(...)` 的参数规格见 [../codegen/special/pending/system/01_data_type.md](../codegen/special/pending/system/01_data_type.md)。
- `msize(matrix_value)` 返回 `array(行数, 列数)`
- `msize(matrix_value, 1)` 返回行索引数组和列索引数组。
- `mrows(matrix_value)` / `mcols(matrix_value)` 默认返回数量;第二个参数写成 `1` 时返回索引数组。
@@ -527,6 +527,380 @@ B
0
```
### 矩阵乘法、除法、乘方:`:*``:/``:^`
`:*` 是矩阵乘法(区别于逐元素乘 `*`):
代码块身份:可直接照写示例
```tsl
a := array((1, 2), (3, 4));
b := array((5, 6), (7, 8));
element_wise := a * b;
matrix_multiply := a :* b;
writeLn("逐元素乘 (0,0):", element_wise[0][0]);
writeLn("矩阵乘 (0,0):", matrix_multiply[0][0]);
writeLn("矩阵乘 (0,1):", matrix_multiply[0][1]);
writeLn("矩阵乘 (1,0):", matrix_multiply[1][0]);
writeLn("矩阵乘 (1,1):", matrix_multiply[1][1]);
```
代码块身份:输出片段
```text
逐元素乘 (0,0): 5
矩阵乘 (0,0): 19
矩阵乘 (0,1): 22
矩阵乘 (1,0): 43
矩阵乘 (1,1): 50
```
说明:
- `*` 是逐元素乘(element-wise),`a * b``(0,0)``1 * 5 = 5`
- `:*` 是真正的矩阵乘法,`a :* b``(0,0)``1*5 + 2*7 = 19`
- 矩阵乘法要求左矩阵列数等于右矩阵行数
`:/` 是矩阵除法(等价于 `A :* !B`):
代码块身份:可直接照写示例
```tsl
a := array((1, 2), (3, 4));
b := array((2, 0), (0, 2));
result := a :/ b;
writeLn(result[0][0]);
writeLn(result[0][1]);
writeLn(result[1][0]);
writeLn(result[1][1]);
```
代码块身份:输出片段
```text
0.5
1
1.5
2
```
`:\` 是矩阵左除(等价于 `!A :* B`,常用于解线性方程组):
代码块身份:可直接照写示例
```tsl
a := array((2, 0), (0, 4));
b := array((4), (8));
result := a :\ b;
writeLn(result[0][0]);
writeLn(result[1][0]);
```
代码块身份:输出片段
```text
2
2
```
说明:
- `:\` 左除用于解线性方程组 `A * X = B`,等价于 `X = A^(-1) * B`
- **右侧 `b` 必须是列向量**(用 `array((4), (8))` 而非 `array(4, 8)`
- `a :\ b` 返回 `array((2.0), (2.0))`,即 `X` 的列向量
- 当 A 行数 > 列数时返回最小二乘解,行数 < 列数时返回一个可行解
`:^` 是矩阵乘方(`A :^ 2` 等价于 `A :* A`):
代码块身份:可直接照写示例
```tsl
a := array((1, 1), (0, 1));
pow2 := a :^ 2;
manual := a :* a;
writeLn("pow (0,0):", pow2[0][0]);
writeLn("pow (0,1):", pow2[0][1]);
writeLn("pow (1,1):", pow2[1][1]);
writeLn("manual (0,1):", manual[0][1]);
```
代码块身份:输出片段
```text
pow (0,0): 1
pow (0,1): 2
pow (1,1): 1
manual (0,1): 2
```
复合赋值算符:
代码块身份:可直接照写示例
```tsl
a := array((1, 2), (3, 4));
b := array((1, 0), (0, 1));
a :*= b;
writeLn(a[0][0]);
```
代码块身份:输出片段
```text
1
```
说明:
- `:*=``:/=``:\=``:^=` 分别是矩阵乘、除、左除、乘方的复合赋值形式
- `a :*= b` 等价于 `a := a :* b`
### 基础函数的矩阵广播与异常处理参数
多参数基础函数支持逐参数广播:
代码块身份:可直接照写示例
```tsl
data := array(1.55, 2.99, 3.85);
precision := array(-1, 0, 0);
result := RoundTo(data, precision);
writeLn(result[0]);
writeLn(result[1]);
writeLn(result[2]);
```
代码块身份:输出片段
```text
1.6
3
4
```
说明:
- `RoundTo(data, precision)` 对两个数组逐元素配对:`RoundTo(1.55, -1)``1.6``RoundTo(2.99, 0)``3.0`
- 规则:为每个参数寻找一个或一组匹配者,逐参数广播
基础函数尾部可追加异常处理参数 `ErrDefine``ErrReplace`
代码块身份:可直接照写示例
```tsl
data := array(4, -1, 9);
result := sqrt(data, 1, -999);
writeLn(tostn(result));
```
代码块身份:输出片段
```text
array(2.0,NAN,3.0)
```
说明:
- `sqrt(data, 1, -999)` 中,第二参数 `ErrDefine=1` 表示允许 NIL 值不允许错误值
- `ErrReplace=-999` 是错误位置的替换值(本例中 `-1` 的平方根为错误,但 `ErrDefine=1` 保留为 NAN
- `ErrDefine` 取值:`0`=不允许错误和 NIL、`1`=允许 NIL 不允许错误、`2`=错误值保留为原值
- `ErrReplace``ErrDefine=0``1` 时生效,用于替换错误/NIL 位置
另一个例子:
代码块身份:可直接照写示例
```tsl
data := array(1, nil, "AAA", -100);
result := abs(data, 0, -999);
writeLn(tostn(result));
```
代码块身份:输出片段
```text
array(1,-999,-999,100)
```
说明:
- `abs(data, 0, -999)` 中,`ErrDefine=0` 不允许错误和 NIL
- `nil``"AAA"` 都被替换成 `-999`
- 对于多参数基础函数,`ErrDefine``ErrReplace` 总是可以作为可选参数添加在最后
### 矩阵转置:反引号 `` ` ``
单次转置交换行列:
代码块身份:可直接照写示例
```tsl
a := array((1, 2, 3), (4, 5, 6));
transposed := `a;
writeLn("原矩阵行数:", mrows(a));
writeLn("原矩阵列数:", mcols(a));
writeLn("转置后行数:", mrows(transposed));
writeLn("转置后列数:", mcols(transposed));
writeLn("转置 (0,0):", transposed[0][0]);
writeLn("转置 (1,0):", transposed[1][0]);
writeLn("转置 (2,0):", transposed[2][0]);
```
代码块身份:输出片段
```text
原矩阵行数: 2
原矩阵列数: 3
转置后行数: 3
转置后列数: 2
转置 (0,0): 1
转置 (1,0): 2
转置 (2,0): 3
```
一维数组转置成列向量:
代码块身份:可直接照写示例
```tsl
b := array(1, 2, 3);
col_vector := `b;
writeLn("一维长度:", length(b));
writeLn("列向量行数:", mrows(col_vector));
writeLn("列向量列数:", mcols(col_vector));
```
代码块身份:输出片段
```text
一维长度: 3
列向量行数: 3
列向量列数: 1
```
双转置把一维数组变成行向量(常用于 `union` 追加行):
代码块身份:可直接照写示例
```tsl
b := array(1, 2, 3);
row_vector := ``b;
writeLn("行向量行数:", mrows(row_vector));
writeLn("行向量列数:", mcols(row_vector));
writeLn("行向量 (0,0):", row_vector[0][0]);
writeLn("行向量 (0,1):", row_vector[0][1]);
writeLn("行向量 (0,2):", row_vector[0][2]);
```
代码块身份:输出片段
```text
行向量行数: 1
行向量列数: 3
行向量 (0,0): 1
行向量 (0,1): 2
行向量 (0,2): 3
```
说明:
- `` `a `` 是后缀算符,写在矩阵变量或表达式之后
- 一维数组 `b` 转置一次成列向量(3行1列),转置两次成行向量(1行3列)
- 双转置技巧常配合 `union` 逐行追加数据
### 矩阵拼接:`union``&=``|``:|`
`union` 按行拼接(一维或二维):
代码块身份:可直接照写示例
```tsl
a := array(1, 2, 3);
b := array(4, 5, 6);
result := a union b;
writeLn(length(result));
writeLn(result[0]);
writeLn(result[3]);
writeLn(result[5]);
```
代码块身份:输出片段
```text
6
1
4
6
```
`&=``union` 的复合赋值形式(注意不是 `union=`):
代码块身份:可直接照写示例
```tsl
a := array(1, 2, 3);
b := array(4, 5, 6);
a &= b;
writeLn(length(a));
writeLn(a[5]);
```
代码块身份:输出片段
```text
6
6
```
`|` 按列拼接:
代码块身份:可直接照写示例
```tsl
a := array((1, 2), (3, 4));
b := array((5, 6), (7, 8));
result := a | b;
writeLn("列数:", mcols(result));
writeLn("(0,2):", result[0][2]);
writeLn("(1,3):", result[1][3]);
```
代码块身份:输出片段
```text
列数: 4
(0,2): 5
(1,3): 8
```
`:|` 对非完全矩阵补 `nil``|` 不补:
代码块身份:可直接照写示例
```tsl
a := array((1, 2, 3), (2, 3));
colon_result := a :| a;
bar_result := a | a;
writeLn("colon 结果列数:", mcols(colon_result));
writeLn("colon (1,2):", colon_result[1][2]);
writeLn("bar 结果 (1,2):", bar_result[1][2]);
```
代码块身份:输出片段
```text
colon 结果列数: 6
colon (1,2): nil
bar 结果 (1,2): 1
```
说明:
- `union` / `&=` 用于行方向拼接
- `|` / `:|` 用于列方向拼接
- 非完全矩阵(行长度不一致)用 `:|` 会在缺位补 `nil`,而 `|` 不补
- 对应的复合赋值形式:`&=`(行并)、`|=`(列并)、`:|=`(列并补 nil
## 默认生成模板
需要矩阵构造时,优先从这个最短模板开始:
@@ -541,12 +915,18 @@ matrix_value := zeros(2, 3);
- 把 `eye(3)` 当成一维数组。
- 把 `!A` 当成逻辑非表达式。
- 把 `*` 当成矩阵乘法;矩阵乘法使用 `:*`
- 把 `:*``:/``:\``:^` 当成逐元素运算;逐元素运算使用 `*``/``^`
- 在 `:\` 左除时,右侧用一维数组而非列向量;右侧必须用 `array((v1), (v2), ...)` 形式。
- 以为 `mrows(matrix_value, 1)``mcols(matrix_value, 1)` 返回的还是数量。
- 写带步长的 `->` 时,漏掉外层 `array(...)`
- 还在普通数组页里硬塞矩阵专用大小接口。
- 把 `::=` 写成带 `begin ... end` 的语句块。
- 用 `::` 期待遍历到任意深度;深度遍历使用 `:.`
- 子矩阵赋值时用形状不匹配的矩阵硬塞。
- 把 `union` 的复合赋值写成 `union=`;正确写法是 `&=`
- 一维数组直接 `union` 期待得到二维结果;需要先双转置 `` ``b `` 变成行向量。
- 在基础函数异常参数时用分号分隔;正确写法用逗号:`sqrt(data, 1, -999)`
代码块身份:反例 / 不可照写
+1 -1
View File
@@ -25,7 +25,7 @@
- `fmarray[...]` 可以直接构造 `FMArray` 常量。
- `dataType(v)``FMArray` 返回 `27`
- `dataType(v, 1)` 可以读出 `FMArray` 单元格类型;本页文档类型包括 `0` 整型、`1` 浮点、`20` 64 位整型。
- FMArray 相关函数的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md) 和 [../reference/catalog/math.md](../reference/catalog/math.md);本页只保留 FMArray 行为示例和返回形态边界。
- FMArray 相关函数的参数规格见 [../codegen/special/pending/system/01_data_type.md](../codegen/special/pending/system/01_data_type.md) 和 [../codegen/builtin/math.md](../codegen/builtin/math.md);本页只保留 FMArray 行为示例和返回形态边界。
- `ifFmarray(v)` 可直接判断值是否为 `FMArray`
- `mInit``mInitDiag``mRand` 都可直接生成 `FMArray`
- `arrayToFm``matrixToArray` 可在 `Array` / `FMArray` 间互转。
@@ -22,8 +22,8 @@
## 核心规则
- 对象二元算符重载的最小可靠形态是成员方法 `function operator + (data);` 这一类写法。
- 比较算符可写成 `function operator < (data, isLeft);`,用 `isLeft` 区分对象在左边还是右边。
- 对象二元算符重载的最小可靠形态是成员方法 `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`
@@ -31,7 +31,7 @@
- `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` / `::` / `:.` 重载,直接当成语法事实
- 不要把未写入文档资料里的裸 `function operator;` / `function operator1;` 直接当成语法事实。`::` / `:.` / `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex` 重载本页已给出可照写形态,照本页示例写即可
## 可直接照写示例
@@ -40,46 +40,49 @@
代码块身份:可直接照写示例
```tsl
t1 := new TComplex();
t1.vReal := 10;
t1.vImaginary := 100;
t2 := t1 + 10;
writeLn(t2.vReal);
writeLn(t1 < 5);
writeLn(t1 < 300);
writeLn(5 < t1);
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 TComplex = class
type Complex = class
public
vReal;
vImaginary;
function operator + (data);
property RealPart read real_part_ write real_part_;
property ImaginaryPart read imaginary_part_ write imaginary_part_;
function operator + (other);
begin
r := new TComplex();
if ifNumber(data) then
sum := new Complex();
if ifNumber(other) then
begin
r.vReal := vReal + data;
sum.RealPart := real_part_ + other;
end
else
begin
r.vReal := vReal + data.vReal;
r.vImaginary := vImaginary + data.vImaginary;
sum.RealPart := real_part_ + other.RealPart;
sum.ImaginaryPart := imaginary_part_ + other.ImaginaryPart;
end
return r;
return sum;
end;
function operator < (data, isLeft);
function operator < (other, is_left);
begin
if ifNumber(data) then
if ifNumber(other) then
begin
v := vReal < data;
less := real_part_ < other;
end
else
begin
v := (vReal ^ 2 + vImaginary ^ 2) < data.vReal ^ 2 + data.vImaginary ^ 2;
less := (real_part_ ^ 2 + imaginary_part_ ^ 2) < other.RealPart ^ 2 + other.ImaginaryPart ^ 2;
end
if not isLeft then v := not v;
return v;
if not is_left then less := not less;
return less;
end;
private
real_part_;
imaginary_part_;
end;
```
@@ -87,7 +90,8 @@ end;
- 依次输出 `20``0``1``1`
- 说明 `obj + value` 可以通过成员 `operator +` 接管
- 说明带 `isLeft` 的比较算符可以同时处理 `obj < value``value < obj`
- 说明带 `is_left` 的比较算符可以同时处理 `obj < value``value < obj`
- 私有成员用尾随下划线的 `real_part_` / `imaginary_part_`,对外用 `PascalCase` property 暴露
代码块身份:输出片段
@@ -104,12 +108,12 @@ end;
```tsl
t := array(1, 2, 3, 4, 5);
b := new bb(t);
b := new IndexableBox(t);
writeLn(b[2]);
b[3] := 999;
writeLn(b.data[3]);
type bb = class
type IndexableBox = class
public
data;
function create(v);
@@ -139,7 +143,7 @@ end;
代码块身份:配置片段 / 概念骨架
```tsl
type bb = class
type IndexableBox = class
public
// 其余字段、create()、operator[1] 和测试主体同上一段
function operator[0](index, s1);
@@ -265,16 +269,16 @@ end;
代码块身份:可直接照写示例
```tsl
b := new bb(10);
++b;
writeLn(b.data);
c := b++;
counter := new Counter(10);
++counter;
writeLn(counter.data);
c := counter++;
writeLn(c.data);
writeLn(b.data);
b += 5;
writeLn(b.data);
writeLn(counter.data);
counter += 5;
writeLn(counter.data);
type bb = class
type Counter = class
public
data;
function create(v);
@@ -285,7 +289,7 @@ public
begin
if v = 0 then
begin
r := new bb();
r := new Counter();
r.data := data;
r.data++;
return r;
@@ -304,22 +308,250 @@ end;
- 依次输出 `11``11``12``17`
- 说明前置 `++` 会直接修改对象状态
- 说明这个最小样例里,后置 `b++` 返回的是递增前快照
- 说明 `operator += (v)` 可以接管 `b += 5`
- 说明这个最小样例里,后置 `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` 的列数
- 关键字函数重载与二进制函数重载写法一致,但类内调用同名关键字函数不需要 `::` 前缀
## 本页不生成的范围
- `::` / `:.` 遍历重载
- `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex`
- 多级 `[]` 下标重载
- 右侧算术如 `value + obj`
- 对基础二进制函数的大规模重载族
这些名称只作为边界提示,不作为本页可生成模板。
## 禁止项
- 不要从本页 `operator` 示例外推未写入文档的重载族。
- 不要把 `mcell` / `mrow` / `mcol` / `::` / `:.` 直接写成可用语法
- 重载 `::` / `:.` 遍历时,不要漏掉配套的 `mcell` / `mrow` / `mcol` / `mIndexCount` / `mIndex` 重载,否则遍历体会报 `override function not found`
- 不要把多级 `[]` 下标重载或 `value + obj` 这类右侧算术写成文档事实。
- 不要在本页发明普通类语法;基础对象模型回 [08_objects_and_classes.md](08_objects_and_classes.md)。
+2 -2
View File
@@ -90,6 +90,6 @@
## 切换到别的层
- 数据仓库金融函数:见 [../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md)
- 数据仓库金融函数:见 [../codegen/dotnet/datawarehouse/](../codegen/dotnet/datawarehouse/)
- 模块 / 集成 / 互操作:见 [../modules/index.md](../modules/index.md)
- 函数库查找:见 [../reference/index.md](../reference/index.md)
- 函数库查找:见 [../codegen/index.md](../codegen/index.md)