playbook/docs/tsl/syntax_book/01_language_basics.md

133 KiB
Raw Blame History

01 语言基础

本章收录 TSL 语言概述、基本构成、数据类型、变量/表达式、字符串与块环境语句等基础语法。

目录

语言概述

内容

  • 关于 TSL 语言
  • TSL 语言的特点

关于 TSL 语言

TSL 是 Tinysoft Statistical analysisLanguage 的简称,她是 Tinysoft Corp.专门为了迎合统计分析所开发的编程语言。TSL 拥有快捷的数据访问方法、强大的运算能力、丰富的函数分析库、灵活强大的数据呈现能力以及强大的数据导入导出功能,拥有了 TSL统计分析将变得更快速更容易。

TSL 语言开始于 2002 年发展伊始TSL 是一种类 Pascal 编程语言它除了包含有作为一种编程语言所应具有的各种逻辑流程控制功能以外更出色的是其具有的丰富的函数库以及方便灵活的数据类型。TSL 使用简单易用的函数而不是复杂的文件 IO 或者数据库作为数据访问的基本途径TSL 无需用户开发数据呈现界面无论是简单的整数实数字符串还是复杂的数组、表或者图形TSL 语言均可以快速地实现。

经过十余年的发展目前已经是一种融合面向对象矩阵计算SQL集合运算等等为一体的集成性语言她兼具 Object pascal 的严谨SQL 的优雅矩阵语言的高效等特性成为了数据处理数据挖掘的最佳选择。更难得的是在历次重大升级中TSL 语言一直秉承兼容性原则,使得 TSL 开发的代码总是可以无损升级到新的版本而矩阵、SQL 等语法和基础语法之间的高度融合亦让 TSL 语言兼具效率与易读性。

TSL 语言不仅仅是一种拥有诸多算符和语法的语言同时还拥有丰富的函数库无论是基础的字符串、数字、日期、数据库、文件等处理函数还是数理统计优化时间序列分析等数学性方法TSL 语言函数包都提供了非常强大的支持。TSL 语言还为证券金融提供了非常丰富的 TOOLBOX 以及 FRAMEWORK涵盖包括数据处理、财务分析、技术分析、风险收益分析、组合评价、归因分析、定价、优化、策略框架、回测框架等等诸多方面。

TSL 语言除了可以用于建模挖掘,还支持用于 WEB 开发,报表引擎,独立脚本等等各个方向的支撑,为交叉学科应用的融合提供了非常好的解决方案。

TSL 语言的特点

与其它的高级编程语言比较TSL 语言的特点可以总结如下:

丰富的专业分析函数支持。专业针对性强。

语法简易,便于学习和轻松掌握。

更强大简便的数据访问功能,更灵活的数据类型。

无需界面开发,可以免除输入输出的用户界面开发之苦。

基础知识

内容

  • TSL 程序的基本构成
  • TSL 的符号
  • TSL 的数据类型
  • 常量与变量
  • 弱类型语言与变量类型运行时相关
  • 运算符
  • 表达式

TSL 程序的基本构成

TSL 程序的基本构成是函数定义体,一个函数定义体中可包含有多个的函数。函数是 TSL 程序的最小可执行的单元,函数的基本结构如下:

Function
 FunctionName([Param1,Param2…]);//函数申明头在关键字Function后带有函数名称函数名称的后面带有括号对其中可带有参数。


Begin
//用Begin作为函数开始标志。

  Statement;

  [Statement;

  ……

  Statement;]

    //函数体,可包括多个语句段。


End;
 //用End作为函数的结束标志。

例子 1、下面是一个最简单的显示当天日期的函数例子

  Function HelloTSL();

  Begin

     dToday := Date(); //将函数Date返回的值赋值给变量dToday

     strToday := 'Hello ,today is' + DateToStr(dToday) + '!';

     //调用函数DateToStr将dToday转换为字符串并合成输出串赋值给//strToDay

     return strToday;//返回strToday。

  End;

本函数又可以简化为如下:

  Function HelloTSL();

  Begin

    Return 'Hello ,today is' + DateToStr(Date()) + '!';

  End;

TSL 的符号

内容

  • 保留字(关键字)
  • 标识符
  • 注释符
  • 编译选项与注释符

保留字(关键字)

保留字是指在 TSL 语言中具有特定的含义,是编写使用前必须了解的。目前 TSL 语言中的保留字一共有:

内容
  • PROGRAM
  • FOR
  • REPEAT
  • SYSTEM
  • TO
  • DO
  • THEN
  • BEGIN
  • WITH
  • STEP
  • ELSE
  • REALPARAMCOUNT
  • FUNCTION
  • PARAMS
  • IF
  • END
  • WHILE
  • PARAMCOUNT
  • DOWNTO
  • SYSPARAMS
  • PROCEDURE
  • Shl
  • DEBUGRETURN
  • BREAK
  • GOTO
  • Ror
  • NIL
  • OR
  • CASE
  • DebugRunEnvDo
  • LABEL
  • DebugRunEnv
  • FALSE
  • UNTIL
  • RETURN
  • EXIT
  • TRUE
  • CONTINUE
  • AND
  • ARRAY
  • Shr
  • Rol
  • OVERLOAD
  • EXCEPTOBJECT
  • LIKEEPS
  • CDECL
  • Sqlin
  • LIKE
  • IN
  • SUDO
  • RAISE
  • EXCEPT
  • SETUID
  • DIV
  • LIKEEPSRATE
  • NOT
  • FINALLY
  • MOD
  • PASCAL
  • TRY
  • FASTCALL
  • VAR
  • ECHO
  • EXTERNAL
  • THISFUNCTION
  • STATIC 静态计算
  • SAFECALL
  • NAN
  • INF
  • OUT
  • STDCALL
  • CONST
  • REGISTER
  • GLOBAL
  • MYMEM
  • MAXMEM
  • MTIC
  • MTOC
  • line
  • __stack_frame
  • 面向对象支持的保留字
  • 远程调用客户端函数的保留字
  • SQL 语法支持保留字
  • 被系统保留未被使用的保留字
PROGRAM

程序开始的入口,一般用户不需要使用,在作为独立的 TSL 脚本时可以使用。

通常来说,倘若编写 TSL 代码作为 CGI 执行运行,系统默认以 PROGRAM 模式运行。虽然 TSL 可以省略 PROGRAM 关键字,但是使用 PROGRAM 关键字可以使得 TSL 代码里包含子函数。

例如:

Program
Test;

   Function sub1();

   Begin

    Writeln('Execute Sub1');

   End;


Begin


   Sub1();


End.
FOR

循环子句,可组成 FOR TO DO 或者 FOR DOWNTO DO 循环子句。

参见TODOWNTO

REPEAT

循环子句,可组成 REPEAT 循环子句。

SYSTEM

系统函数前缀,因为系统函数可能被用户函数重载,如果要指定调用系统函数,可以用 system.funcname 的模式。例如 system.close()是指定调用系统的收盘价函数。

参见:用户和函数相关函数

TO

循环子句往后,可组成 FOR TO DO 循环子句,也可以用于 CASE 和 SELECT 语句中。

参见FORDOWNTO

DO

循环子句做,可组成 FOR TO/DOWNTO DO 或者 WHILE DO 循环子句。此外DO 还可以组成 WITH DO 设置语句的运行时环境。

参见WHILEFORWITH

THEN

条件子句那么,可组成 IF THEN 或者 IF THEN ELSE 条件子句,详细见 IF

BEGIN

语句段开始,与 END 组合成 BEGIN END 语句段,详细见 END

WITH

为语句设置运行时环境,可组成 WITH DO在类 SQL 语法中,可在 JOIN 子句中使用 with(expLeft…,expRight)作为快速条件。

参见DOWith 设值运算符块环境设置语句WITH ON 条件。

STEP

For 循环语句里的步长FOR 的缺省步长为 1。

参见FORTODOWNTO

ELSE

条件子句否则,可组成 IF THEN ELSE

参见IFTHEN

REALPARAMCOUNT

系统内置变量,用来得到函数调用时实际被送入的参数个数。用于下标方式获取参数。

例如:

  a:=function(i1,i2)

   begin

   echo '实际送入的参数:',realparamcount;

   echo '参数:',paramcount;

   return 1;

   end;

  return Call(a,1);

//结果:

参见PARAMS

FUNCTION

函数声明开始,组成类似于 FUNCTION XXXX(); BEGIN END;的函数块

PARAMS

系统内置变量,可以以序列的方式访问函数的参数。例如 Params[1]表示参第一个参数Params[n]表示函数的第 n 个参数。参数的个数可以用 ParamCount 获得。

对于一般的应用,我们建议您使用参数名称来访问参数。这个功能为了编写更强大的非固定参数个数的函数而设置的,系统支持调用无参数定义的函数带入不定个数的参数,这样,送入的参数无法通过常规的名称来访问。用户可以用 ParamCount 获取参数的个数,通过 Params[n]来访问每个参数。

参见PARAMCOUNT

IF

条件子句如果,可组成 IF THEN 或者 IF THEN ELSE 条件子句,详细见 THEN

END

语句段结束,与 BEGIN 组合成 BEGIN END 语句段

WHILE

循环子句,可组成 WHILE DO 循环子句,

参见TO

PARAMCOUNT

系统内置变量,用来得到函数的参数个数。用于下标方式获取参数。

参见PARAMS

DOWNTO

循环子句往前,可组成 FOR DOWNTO DO 循环子句,

参见FORTO

SYSPARAMS

用语系统环境变量存贮的数组。

除了特殊的环境变量以外,直接访问 SysParams 的下标利用 SetSysParam 和 GetSysParam 的功能相同。

例如SysParams["MM"]:=1;等同于 SetSysParam("MM",1);

直接使用 SysParams 来访问和设置系统环境的值的性能会优越于 SetSysParam 和 GetSysParam 函数,尤其当某系统环境变量的值是一个数组的时候。

参见:系统参数设置和获取函数

PROCEDURE

与 FUNCTION 类似,但是在函数头后不允许加返回类型值。

Shl

左移位运算符X Shl Y二进制 X 左移 Y 位

范例:

return 6 shl 3;


//结果48


//返回十进制 (6=110) 左移3位后得到二进制110000十进制是48
DEBUGRETURN

调试返回,后面跟返回值,可在任何地方直接将结果返回到客户端,而不是象 RETURN 一样返回到上一级别。

BREAK

终止循环语句段,可用于 FOR 或者 WHILE 循环中,

参见循环语句FORWHILE

GOTO

无条件跳转语句,需要 LABEL 配合,参见 GOTO

Ror

循环右移位运算符X Ror Y二进制 X 循环右移 Y 位, 原来的低 Y 位变成高 Y 位

范例:

Return 18 Ror 1;

//结果9

//返回十进制 (18=10010) 右移1位后得到二进制1001十进制是9
NIL

数据类型为 NIL 的常量

OR
CASE

多分支条件语句CASE OF

DebugRunEnvDo
LABEL

无条件跳转的标号定义语句,参见 GOTO

DebugRunEnv
FALSE
UNTIL

循环子句 REPEAT 的结束判断符。

RETURN

函数返回,后面可跟返回值

EXIT
TRUE
CONTINUE

循环语句段中继续,可用于 FOR 或者 WHILE 循环中,

参见循环语句FORWHILE

AND
ARRAY

数组类型初始化方法为Array(下标 0:取值 0,下标 1:取值 1,…,下标 N:取值 N),当下标省略的时候,默认下标从整数 0 开始,例如 Array(1,2,3,4)相当于 Array(0:1,1:2,2:3,3:4),数组下标支持字符串,例如 Array('a':1, 'b':2)多维数组的声明例子Array(('a':1,'b':2),('a':2,'b':3)),其规则为取值为数组,在 ARRAY 声明中的数组取值不再需要 ARRAY 关键字。注意:如果即无下标也无取值,则为空数组,例如 Array(),数组类型

参见ArrayTableArray 数组类型

Shr

右移位运算符X Shr Y二进制 X 右移 Y 位

范例:

Return 18 shr 3;

//结果2

//返回十进制 (18=10010) 右移3位后得到二进制10十进制是2
Rol

循环左移位运算符X Rol Y二进制 X 各位左移 Y 位,原来的高 Y 位变成低 Y 位

范例:

return 6 rol 3;


//结果48


//返回十进制 (6=110) 左移3位后得到二进制110000十进制是48
OVERLOAD
EXCEPTOBJECT

获得异常对象实例,异常对象包含 ErrNo,ErrLine,ErrInfo 三个属性。参见错误控制,以及调试语句

LIKEEPS

LIKE 的精度设置,可以:=进行设置,也可以获得其值,默认值为 1e-6

参见LIKE,LIKEEPSRATE

CDECL
Sqlin

行记录的存在于判断。

语法v SQLIn R

其中v 一定是 R 的一条记录才是真。以一条记录为判定单位。

范例一:

v:=array(1,2);

r:=array((1,2),(2,3));

return v sqlin r;//返回结果为1

范例二:

v:=array(1,3);

r:=array((1,2),(2,3));

return v sqlin r;//返回结果为0

范例三:与 In 的区别。

a:=array();

a["1 ifin array(1,2)"]['In']:=1 in array(1,2);

a["1 ifin array(1,2)"]['sqlin']:=1 sqlin array(1,2);

a["1 ifin array((1,2))"]['In']:=1 in array((1,2));

a["1 ifin array((1,2))"]['sqlin']:=1 sqlin array((1,2));

a["array(1,2) ifin array((1,2),(2,3))"]['In']:=array(1,2) in array((1,2),(2,3));

a["array(1,2) ifin array((1,2),(2,3))"]['sqlin']:=array(1,2) sqlin array((1,2),(2,3));

a["array(1,3) ifin array((1,2),(2,3))"]['In']:=array(1,3) in array((1,2),(2,3));

a["array(1,3) ifin array((1,2),(2,3))"]['sqlin']:=array(1,3) sqlin array((1,2),(2,3));

return a;

//返回:
内容
  • Not sqlin
Not sqlin

Not sqlin 是与 Sqlin 相对应的操作符,两者语法结构相似但逻辑功能完全相反。

功能:以行的模式来判定是否不存在于数组中。

语法v Not SQLIn R

其中v 一定不是 R 的一条记录才是真。以一条记录为判定单位。

示例:

v:=array(1,2);

r:=array((1,2),(2,3));

return v not sqlin r;//返回结果为0
v:=array(1,3);

r:=array((1,2),(2,3));

return v not sqlin r;//返回结果为1
LIKE

字符串模式匹配判定符,判定是否匹配于指定的正则表达式。

例如: '2009-1-1' like "\d+-\d+-\d+" 返回为真

实数 LIKE例如 1.000001 like 1 为真,实数相等一般要避免用等于(=)来判断

Like 的计算方法如下:

如果两个数的差的绝对值<likeepslike 为真

或者该绝对差/两个数的绝对数的平均值< likeepsrate like 为真

默认的 likeeps 以及 likeepsrate 均为 1e-6。

在 TSL 中, Like 符和 SQL 的 Like 的含义不同, TSL 的 Like 的右元素为正则表达式,左边运算元素为匹配串,结果是左边串是否匹配右边的正则表达式

参见:正则表达式,LIKEEPS,LIKEEPSRATE

内容
  • Not Like
Not Like

2025/8 月版本开始TSL 语言中支持 NOT LIKE 这样 not(a like b)可以简单写成 a not like b

Not Like 是与 FAQLIKE 相对应的模式匹配操作符,两者语法结构相似但逻辑功能完全相反。

字符串中的 Not Like

Not Like 可用于判断某个字符串是否不符合指定的模式规则,当且仅当字符串与给定模式不匹配时返回 True。

例如:

if a not like "\\d{4}-\\d{2}-\\d{2}" then

begin

  return "a不包含'YYYY-MM-DD'格式日期字符串";

end

else return "包含'YYYY-MM-DD'格式";

数值中的 Not Like

Not Like 可用于判断某个数值是否和指定值相似,当且仅当数值与给定模式不相似时返回 True。

例如 1.000001 not like 1 为 False。

计算方法和 FAQLIKE 类似,可参考其中的介绍。

IN

存在于判定符,判定是否在数组中。是以最小的元素进行判断。

例如 1 in array(1,2,3) 返回真0 in array(1,2,3)返回假。

IN 也支持判定是否在多维数组中存在。

内容
  • Not IN
Not IN

Not IN 用于判断指定元素是否不存在于数组中。若该元素在数组中未被找到,则返回 True否则返回 False。

与 IN 操作符的使用方式类似,但意义相反。同时也支持如下特性:

1、支持被查找数组为多维数组如 1 not in array((1),(2))返回值为 False

2、支持子集判断如 array(1,2) not in array(1,2,3,4)返回值为 False

示例:

   t1:=1 not in array(1,2,3);

   t2:=0 not in array(1,2,3);

   return array(t1,t2);

返回结果array(0,1)

SUDO
RAISE

出错并终止运行,参见错误控制,以及调试语句

EXCEPT

异常处理。参见错误控制,以及调试语句

SETUID
DIV

除取整运算符,详见算术运算符

LIKEEPSRATE

LIKE 的精度设置,可以:=进行设置,也可以获得其值,默认值为 1e-6

参见LIKE,LIKEEPS

NOT
FINALLY

最终执行处理。参见错误控制,以及调试语句

MOD

余运算符,与%的意义相同,详见算术运算符

PASCAL
TRY

定义被保护的块。参见错误控制,以及调试语句

FASTCALL
VAR

在调用函数的时候,在参数之前标识,与 OUT 一样,标明是可修改参数。除非系统使用 VarByRef 编译选项,否则默认为可修改参数。

VAR 还可以用来声明变量,在使用 Explicit 编译选项的时候,变量必需提前声明。

ECHO

输出内容(输出到控制台或者 WEB 应用输出到浏览器语法echo [,…],输出的内容必需是简单类型,否则仅仅只是输出一个基本的类型信息。

echo 'Today is ',DateToStr(today()),'now->',timetostr(now());

如果在平台运行,会输出信息到客户端

固定功能用法:

Echo #127 可清除掉客户端 ECHO 窗口中的内容。

范例:

范例一:

//打印当前时间


echo '现在时间:',datetimetostr(now())

范例二:

//清除打印窗口的信息,清除命令会在30s后执行


echo '待清除信息';


Sleep(30000);


Echo #127;
EXTERNAL

说明函数是外部函数,来自于其他的动态库。

THISFUNCTION

THISFUNCTION 返回一个函数类型

THISFUNCTION 可得到当前运行的函数

THISFUNCTION(FuncName)可以得到指定 FuncName 的函数

THISFUNCTION(Object.FuncName)可以得到指定对象的方法。

THISFUNCTION(CLASS(ClassName).FUNCNAME)可以得到指定类的类方法。

参考:表达式相关函数

STATIC 静态计算

语法STATIC Expression [name nameExpression]

功能:指定后边的表达式为静态计算(常量计算),该计算只计算一次,用于加速。

其中,

Expression :为表达式

nameExpression为该静态计算标识符每个标识符代表的表达式只执行一次即若该标识符在第二次被调用时则直接返回第一次执行的结果。

例 1

  Begin

    Return Teststatic("BBBB");

  End;

  Function teststatic(key);

  Begin

//这个变量A的值是一个不需要每次调用重新构造的静态计算即它右边的表达式不管上层调用多少次它只执行一次

    A:=
static array("ABCD":1,"BBBB":2,"CCCC":3……..);


    Return A[key];

  End;

例 2

  Begin

    Return staticstockname("SZ000002");

  End;

  Function staticstockname(key);

  Begin

    Return
static StockName(key) name "stkname"$key;
 //每个股票代码求名称都只要计算一次假如StockName函数耗费的时间比较长则静态计算可以加速。

  End;
SAFECALL
NAN

NOT A NUMBER,非数字,例如 0.0/0.0 的结果就是 NAN

INF

无穷大,例如 1/0.0 的结果就是 INF

OUT

在调用函数的时候,在参数之前标识,与 VAR 一样,标明是可修改参数。除非系统使用 VarByRef 编译选项,否则默认为可修改参数。

STDCALL
CONST

功能一:与 OUT 相反,标明是不可修改参数。

功能二:定义常量的关键字。

REGISTER
GLOBAL

指定后边的变量类型为全局变量,全局变量可以在不同的函数中访问到。

例:

Begin

global x,y;

x:=100;

testglobal();

End;

Function testglobal();

Begin

global x;

echo x;

End;

//使用了全局变量之后,以上的 testglobal 函数会打印出 100。

MYMEM

应用已经使用的内存大小。

MAXMEM

应用允许使用的最大内存大小。

MTIC

高精度计算时间,得到 CPU 运行过的计数周期,并设置默认的开始计时,一般会利用 MTOC 来计算耗费的时间。

MTIC;

A:=0;

For i:=0 to 99999 do

A++;

Return MTOC;

以上代码可以计算所耗费的秒数

MTOC

高精度计算时间,得到和上一次调用 MTIC 到当前所耗费的时间,为一个浮点秒数,也可以带参数指定计算的开始点,一般开始点由 MTIC 获得。

内容
  • MTOC
  • MTOC(TICK:double)获得与某个指定的 TICK 之间的秒数
MTOC

获得与上一次调用 MTIC 之间的秒数。

MTIC;


A:=0;


For i:=0 to 99999 do


A++;


Return MTOC;

以上代码可以计算 mtic 和 mtoc 之间的代码执行所耗费的秒数。

MTOC(TICK:double)获得与某个指定的 TICK 之间的秒数

默认 MTOC 和上次 MTIC 匹配,但是也可以指定某个 MTIC 的返回来计算时间

T1:=MTIC;


For i:=0 to 9999 do


A++;


TE1:=MTOC(T1);


MTIC;


For j:=0 to 9999 do


A++;


TE2:=MTOC(T1);


Return array(TE1,TE2,MTOC);
line

关键字,获得当前的行号。

例如echo line;

__stack_frame

说明:获得调用的堆栈的函数名以及行号

返回结构解析

面向对象支持的保留字

关于各个关键字的详细内容见面向对象 TSL 章节

参见Object TSL

内容
  • TYPE
  • CLASS
  • FINDCLASS
  • FINDFUNCTION
  • FACKCLASS
  • IS
  • PROPERTY
  • SELF
  • VIRTUAL
  • OVERRIDE
  • PROTECTED
  • PUBLIC
  • PRIVATE
  • PUBLISHED
  • STATIC
TYPE

用来定义类:

Type abcd=class

Public

V:Integer;

Function returnV();

Begin

Return V;

End;

End;

CLASS

类关键字,用来定义类,见 TYPE。

也可以返回指定的类,如 class(abcd)

也可以用来得到强制类型的对象,例如 class(abcd,aObject),返回强制对象 aObject 为 abcd 类。

FINDCLASS

查找类,与 CLASS 用法类似。

返回指定名称的类FindClass("abcd")

得到强制类型的对象FindClass("abcd",aObject),返回强制对象 aObject 为 abcd 类。

与 CLASS 关键字不同,当不存在指定的类的时候,或者不是指定对象的父类时,并不抛出异常,而是返回一个整数 0。

FINDFUNCTION

查找函数,与 FINDCLASS 用法类似。

返回指定名称的函数FindFunction("Function1")。

返回指定对象的类方法FindFunction("Method1",aObject),返回对象 aObject 的 Method1 方法。

返回指定单元对象findfunction(UnitName),返回单元对象 UnitName。

当找不到指定的函数,并不抛出异常,而是返回一个整数 0。

FACKCLASS

虚假类,仅仅用来做类描述。

IS

判断符,判断对象是否是指定类的实例。

语法Object Is Class

返回:真假。

####### 内容

  • Not IS

####### Not IS

not is 用于判断是否不是指定对象的实例。若不是指定对象的实例,则返回 True否则返回 False。

与 FAQIS 语法结构相似但逻辑功能完全相反。

示例:

  a:=1;

  ret:=a not is class(TStringList);

  return ret;//返回结果1
  a:=new TStringList();

  ret:=a not is class(TStringList);

  return ret;//返回结果0
PROPERTY

定义对象的属性,详细内容见面向对象 TSL。

语法Property A[:String] read GETA write SETA;

 Property A[:String] Index 1 read GETA Write SETA;
SELF

SELF 得到当前的对象。

VIRTUAL

虚方法关键字,指定的函数可被重写。

OVERRIDE
PROTECTED

可视域为受保护的,只有自己当前类和继承类可以访问。

PUBLIC

可视域为公共的,大家都可以访问

PRIVATE

可视域为私有的,只有自己当前类可以访问。

PUBLISHED

可视域为发布,与 PUBLIC 可视域相同

STATIC

指定后边的成员变量类型为静态,静态成员变量是所有的实例共享的全局的,当一个类需要使用到公共变量的时候可以采用静态成员变量。

远程调用客户端函数的保留字
内容
  • RDo2
  • RDo
RDo2

注: Rdo2 在客户机运行的时候,需要用户在权限上许可,具体可参考:平台模型远程调用客户端函数,访问客户端的资源

范例

If Rdo2 InputQuery("nput","Hint",Value) then

 Return Value

Else

 Return "Canceled";

可以在本机弹出一个输入的对话框并返回输入的串如果取消则返回“Canceled”

RDo
SQL 语法支持保留字
相关的关键字 含义
Select 查询语句指定由查询返回的列。详细使用方法见SELECT查询
VSelect 查询语句,返回一个值
SSelect 查询语句,返回一维数组
MSelect 查询语句返回Matrix类型数组
Distinct 取唯一值,去重复项
SelectOpt 返回指定类型值selectopt(type)type值不同返回不同类型的值。详见select字句语法。
DRange Drange(N to M): 从查询结果集中输出从N到M之间的行 Drange(N of M): 从查询结果集中输出从 (N-1)/M * 记录总条数到 N/M * 记录总条数 -1 之间的行;
As 对列命名
From 数据的来源表
MarketTable 高频行情数据表
InfoTable 财务类数据表关键字
TradeTable 交易明细数据表
SqlTable 用于与第三方数据库交互的关键字
HugeSqlTable
KeepNull
Datekey 取行情数据时,指定日期关键字
Of 取数据时,指定股票关键字
Order 排序与by同时使用order by
By 排序与order同时使用order by
Where 条件
Desc 排序,逆序
Asc 排序,正序
Group 分组
Having 聚集
ChecksumOf 生成哈希索引
Countof 记录统计
SumOf
MaxOf 最大值
StdevOf 标准差
VarOf 在险价值
TotalVarOf
NormOf 平方和的平方根
MedianOf 中值
AveDevOf 离散度
GeoMeanOf 几何平均值
SkewOf 偏度
KurtosisOf 峰度
Skew2Of 偏度
Kurtosis2Of 峰度
LargeOf 最大值
PercentileOf 百分点
QuartileOf 四分位的值
TrimMeanOf 返回数据集中的内部平均值,从头尾去掉指定百分比的数据点,再求平均值
CountOf 记录个数
AvgOf 平均值
MinOf 最小值
AggOf 利用回调函数计算聚集的值
StdevpOf 统计标准偏差
VarpOf 统计方差
ModeOf 众数
DevSqOf 样本平均值偏差的平方和
HarMeanOf 调和平均值
Checksum_AggOf 校验和
SmallOf 最小值
PercentRankOf 数据集中X排位的百分点的值
Rankof 排位
FrequencyOf 数值区间在数据集中出现的频率
ProductOf
RefOf 往前取数据
RefsOf
AggValue 当前参与聚集的列的值
ThisGroup 指定分组
ThisRow 当前行的数据值
ThisRowIndex 当前行的相对索引值
ThisOrder 当前排序
Insert 插入数据
InsertFields 插入数据的字段列表
Values 设置或者取得Name=Value模式串中指定的Name的Value值
Update 修改数据
Set 修改数据
Delete 删除数据
DeleteOpt 设定删除数组时是否自动修改下标SelectOpt对稀疏矩阵用的时候很有意义
FetchFirst 查询第一条记录
FetchNext 查询下一条记录
被系统保留未被使用的保留字

EXPORTS

DISPINTERFACE

LIBRARY

ASM

RECORD

RESOURCESTRING

THREADVAR

CONSTRUCTOR

DESTRUCTOR

INLINE

PACKED

ABSTRACT

INHERITED

标识符

标识符的定义:标识符就是以字母开头的字母数字序列,大小写不敏感。可以用来标示常量、变量、程序、函数等。

例如 TSL 程序的基本构成示例中的 HelloTSLdToday(变量名)、strToday(变量名)都是标识符。

标识符的命名规则:

1选用的标识符不能和保留字相同。

2在定义标识符时可以用的字符

A—Za—z0—9

注释符

注释符是什么呢?注释符注释的部分是仅仅给程序员注释所用,对于运行而言是毫无意义的,只是为了阅读程序理解更方便。

TSL 语言支持四种主要的注释符

//是表明到换行为止都是注释内容。

#!为了支持纯 CGI#!在 TSL 中与//一样的用途

{}在左大括号和右大括号之间的内容是注释内容。

(* *)在(*和*)之间的内容是注释内容。

为什么需要功能完全一样的{} (**)注释符呢?

这是用于嵌套注释用的,假定我们已经在某个小段里使用了{}注释符,又要把大段都注释起来,例如:

  A:=0;

  {

  注释这大段代码

   A:=A+1;

  {

  小段注释上边的代码是A赋值等于A+1

  }

  A:=A*A;

  }

结果我们发现,根本无法正确运行,因为第一个注释的开始符{会匹配到第一个}这样A:=A*A;成为注释外的代码,并且最后一个}成了多余。为了解决这个问题,我们可以使用如下模式:

  A:=0;

  (*

  注释这大段代码

   A:=A+1;

  {

  小段注释上边的代码是A赋值等于A+1

  }

  A:=A*A;

  *)

这样就没有问题了,这就称之为嵌套注释。

编译选项与注释符

在 TSL 语言中,为了改变编译的行为,会有一些编译选项,而编译选项也是利用注释符来实现的。

TSL 语言的编译选项和 PASCAL 语言类似,目前支持下列几种用法:

内容
  • {$CompileOption}编译选项模式
  • 条件编译
  • {$dependency class1,class2}依赖关系编译选项
{$CompileOption}编译选项模式

例如{$VarByRef-}可以改变函数调用的入口参数为形参,而{$Explicit+}则可以使得变量使用前必需申明,{$GridCompute-}可以关闭掉#的网格操作符。这些内容将在后续相关章节中详细提到。

在 C 语言中,采用#CompileOption 的形式作为编译选项,大部分语言都支持编译选项以改变编译的默认行为。

参考VarByRef 编译选项Explicit 编译选项

条件编译

TSL 语言使用{$DEFINE} {$IFDEF}{$IFNDEF} {$ELSE} {$ENDIF} {$UNDEF}进行条件编译。

{$DEFINE Identifier} 定义标识

{$UNDEF Identifier} 取消定义标识

{$IFDEF Identifier} 判定是否定义了标识

{$IFNDEF Identifier} 判定是否未定义标识

{$ELSE} 在{$IFDEF}以及{$IFNDEF}条件编译中作为否定条件进入语句

{$ENDIF} {$IFDEF}或者{$IFNDEF}条件编译的结束

例如:

{$DEFINE SampleID}
//定义一个名为SampleID的编译标识


{$IFDEF SampleID}
 //判定是否定义了SampleID

Echo "Defined SampleID";

{$ELSE}
 //否则(如果未定义)

Echo "1 Undefined SampleID";

{$ENDIF}
 //IFDEF的结束


{$UNDEF SampleID}
//此处将SampleID的定义取消


{$IFDEF SampleID}


Error codesdjk sdcnkjsdnksd //此处是错误代码由于在条件编译中未定义SampleID所以此块不会被编译不会出错

{$ELSE} //否则(如果未定义)


Echo "2 Undefined SampleID";

{$ENDIF}

运行打印结果为:

Defined SampleID

2 Undefined SampleID

内容
  • 条件编译和 IF THEN 语句的差异
  • 预定义的条件编译标识
条件编译和 IF THEN 语句的差异

条件编译是仅仅编译符合条件的内容,而不像 IF 需要运行时判断,效率更高。

条件编译中的不需要编译的代码即便是有错误代码块也可以编译成功,这样可以很简单用条件编译的模式处理那些未完成代码。(比使用注释好,采用注释存在注释嵌套问题)

预定义的条件编译标识

tslnewtechcache

如果定义了该标识,表明技术指标相关函数支持缓存定义

tslver31

如果定义了该标识,表明当前支持版本 3.1 的语法

nilinvoke

如果定义了该标识,表明当前支持 calcctrlword 功能

cov

如果定义了该标识,表明当前支持双序列聚集和时间序列函数

commaexpr

如果定义了该标识,表明当前支持逗号表达式

weakptr

如果定义了该标识,表明支持弱引用

AutoWeak

如果定义了该标识,表明支持自动弱引用

例如:

{$IFDEF commaexpr} //判定是否定义了commaexpr

Echo (a:=1,a+=100,a);

{$ELSE} //否则(如果未定义)

Echo "不支持逗号表达式";

{$ENDIF} //IFDEF的结束

若当前环境支持逗号表达式则打印结果为101

否则打印结果为:不支持逗号表达式

{$dependency class1,class2}依赖关系编译选项

一个类如果依赖另上一个类,在类的定义里的第一行可以用该编译选项加入依赖关系。

语法:{$dependency class1,class2}

目前,该依赖关系仅在 TSL 的编辑器中用于自动完成中。用来决定当前开发中的函数自动完成的标识符和函数需要引入哪些其它的类。

例如,在编辑一个函数时,给函数的参数中输入一个实例对象变量,由于天软中变量都是弱类型,无法通过该变量快速查找到目标类的相关方法等信息。

此时,可通过在函数中增加{$dependency class1,class2}语句,对指定类的信息进行加载,使得在编辑函数时,能够通过不完整的函数名自动搜索到相关模型,并补充完整。

具体表现如:

TSL 的数据类型

数据是程序设计的一个重要内容,一般的高级程序语言都提供了种类繁多的数据类型,用来完成不同特色的程序设计。数据类型复杂,往往会使初学者不容易全面和深入掌握。

TSL 语言的数据类型从简洁和实用性考虑目前提供的数据类型分为简单类型、扩展类型其中简单类型分为整型、实型、布尔型、字符串型和日期型二进制流类型扩展类型分为数组型、NIL 型、TGraph 型、TGraphGroup 型和表达式型。

内容

  • Integer
  • Int64
  • Real
  • Boolean
  • TDateTime
  • String
  • Binary
  • ArrayTableArray 数组类型
  • TMatrix
  • Matrix
  • NIL
  • TGraph
  • TGraphGroup
  • TExpression
  • TFUNCTION
  • TCLASS
  • ComObj
  • TSLObj

Integer

整数型一个整型数据用来存放整数TSL 语言中的整型数据是一个带符号的 32 位数据,其可以表达的数值范围是-2147483648..2147483647。

整数的基础含义每个读者都明白,和实数类似的,在 TSL 中,用户一般也无需理解整数的具体表达方式,直接使用就可以了。

但是和实数一样,当处理字节流或者和 DLL 发生关系的时候,整数在计算机中的具体表示就显得重要了。

其他计算机里的整数类型:

Byte,ShortInt字节短整顾名思义存贮为 8 位,占用一个字节。可表达的范围为:有符号为-128 127无符号为 0 255。所谓有符号或者无符号是指出是否需要可以表达负数。

Word,SmallInt小整存贮为 16 位,占用两个字节。可表达的范围为:有符号-32768 32767无符号 0 65535。

Dword,LongInt双子长整存贮为 32 位,占用四个字节。可表达的范围为:有符号-2147483648 2147483647无符号 0 4294967295。所谓 2G4G 就是指的有符号或者无符号下 32 位最大的表达大小。

Int6464 位整数,存贮为 64 位,占用 8 个字节,可表达范围为:-2^63 2^63-1。

TSL 里默认采用的整数是有符号的 LongInt但是提到整数运算不得不提到关于绝大多数语言里的整数溢出问题。例如在 C 语言中,如果整数运算越界了,例如无符号的 4294967295+1结果为 0。在 TSL 语言中,为了应用开发的简便性,当溢出的时候,数据会自动转为浮点数来处理,变为浮点的 4294967296。所以一般情况下用户可以不用理会数据运算溢出问题。当然特殊情况下例外假如用户需要将运算结果存贮到数据库或者文件字节流中由于运算溢出了而原来设计存贮的数据类型不足以表达依旧会产生问题。

TSL 语言的整数常量支持如下四种表达方法:

直接由数字表达十进制,如 100 表示十进制 100。

由数字 E 密表达十进制,如 1E2 表示十进制 100。

由 0x 开头表示十六进制,如 0x100 表示十进制的 256。

由 0b 开头表示二进制,如 0b100 表示十进制的 4。

由 0o 开头表示八进制,如 0o100 表示十进制的 64。

Int64

64 位整数,当 Integer 常量超过 32 位表达的时候,会自动采用 64 位整数,除此以外,也可以采用在整数常量末尾加上 L 来表示 64 位整数,例如 100L 表示值为 100 的 64 位整数。

Real

实数型一个实型数据用来存放实数TSL 语言中的实型数据是一个带符号的 64 位数据,其可以表达的数值范围是 5.0e-324..1.7e308,有效位数是 15..16。由于 TSL 的类型无需要申明因此实数常量的书写方法必须带有小数点例如0 表示整数 0而 0.00 则表示实数 0。

TSL 语言的实数常量除了直接使用数字加小数点外,还可以用例如 1.10E10 这种科学记数法。

此外,当一个未含有小数点的 10 进制整数表达方式超过了 32 位整数所表达的范围TSL 语言将自动将其当做实数常量。

内容
  • 实数的含义
  • 科学计数法
实数的含义

实数的基础含义每个读者都应明白,为了用户使用方便,在 TSL 中用户一般不需要去理解关于实数更多的知识,只需要使用就可以了。

但有例外,一个是将实数处理成为要存贮的字节流,写入文件或者和其他的外部程序发生交互的时候,需要理解实数在计算机中的具体存贮大小。二是和外部 DLLDynamic Link Library一种在 Windows 中经常使用并依赖的开发技术,使得开发可以模块式开发)发生关系的,必需明确实数的计算机里的标准类型。

在计算机里,实数一般是以浮点的方式来表达的,关于浮点的表达方式在这里就不做具体描述。我们只关注下具体的数据类型,一般存在如下数据类型:

Single 也叫 Float单精度浮点数存贮位数为 32 位,也就是占用四个字节的存贮空间,可表达的范围为:负数为-3.4E38 -1.4E-45正数为 1.4E-45 3.4E38,有效精度为十进制的 7 位。目前,由于浮点协处理已经成为计算机的标准配置,计算机内存以及硬盘以及变得很大,这种损失精度的类型已经很少使用了,主要应用在那种对存贮空间很苛刻,对于精度要求不大的应用中。

Double双精度浮点数存贮位数位 64 位,也就是占用八个字节的存贮空间,可表达的范围为:正数为 5.0E-308 1.7E+308负数为-1.7E+308~-5.0E-308有效精度为十进制的 16 位。Double 是目前应用得最广的数据类型,也是 TSL 语言中默认采用的实数数据类型。当和 DLL 发生关系的时候又或者执行字节流处理的TSL 会自动按照指定的位数或者类型采用 DOUBLE 或者 Single 类型。

其他实数数据类型:有占用 16 个字节的 Long Double 类型,有占用 10 个字节的 Extended Double 类型,这些类型可以描述更精确地数字,可以表达更大的数据范围,但是这些类型并不常用。

特殊的实数

在实数中,有几个特殊的实数,-INF,+INF,NaN。也就是负无穷大正无穷大以及错误的数据(Not a Number)。

这些为什么放在实数中呢?事实上这些数据往往都是实数运算产生的结果,例如正数处以 0 得到的是+INF负数处以 0 得到的是-INF而 0 处以 0 则是 NaN。

TSL 语言提供了一些方法给用户去判断是否是这些特殊的实数。

科学计数法

支持科学计数法

a*10^n 可以用 aEn 来表达。

例如 123 可以写为 1.23e20.01234 可以写成 1.234e-2。

Boolean

在计算机中Boolean 类型事实上是被使用得最多的类型,只是很多是隐式使用。例如所有的逻辑判断的结果都是一个 Boolean 类型,而计算机程序中到处都充斥着逻辑判断。

TSL 的 Boolean 型的内部存贮其实只是一个整数1 为真0 为假。但是与 C 语言类型类似,对于数字,非 0 为真0 为假。此外NIL 类型也被判断为假。

其他语言中,按照布尔类型占用的大小,有 LongBool 和 ByteBool 之分,其实,要表达一个布尔类型,一个位就足够了,这也叫 BitBool事实上在汇编语言中经常判断某个指定位是否为 1这其实就是 BitBool。BitBool 基本上不存在在独立的数据类型只是某种按存贮的方式。TSL 如果要和外部的语言的 Boolean 类型打交道,最可靠地方法是采用具体位数的整数。

Boolean 型的常量有两个TRUE 表示为真FALSE 表示为假。

在 TSL 中,除了 0 为假以外NIL 类型、空字符串、空数组在作为逻辑判断的时候也是为假的。

TDateTime

日期类型是一种难以表达的类型,也许有的人说是用某年某月某日的表达方法不是很简单吗,但是计算机语言是无国界的,有的国家是用 12/30/2010 来表达 2010 年 12 月 31 日,有的国家则是用 2010-12-30 来表达,所以采用字符串的方式来描述是难以行得通的。

TSL 中采用数字来描述的日期类型,具体规则如下:

整数部分表示自从 1899 年 12 月 30 日以来经历的天数,小数部分表示一天中均匀的分割 24 小时的时间所形成的时间。

例如:

0 表示 1899 年 12 月 30 日 000

2.75 表示 1900 年 1 月 1 日下午 600

-1.25 表示 1899 年 12 月 29 日 600

要得到一个指定日期的日期值,可以通过如 StrToDate('2002-1-1')的方法获得。

如果要显示一个日期型,请参考相关函数,如 DateToStr,DateTimeToStr 等

TSL 还支持 20101231.0931T 表达 2012 年 12 月 31 日 9 点 31 分。

这种表达方式可以达到最高 10 毫秒的精度。没有小数点即为 0 点 0 分, 20111231T 则为 2011 年 12 月 31 日 0 点 0 分。

采用这样的方式,历史和未来的任何一天的时间都可以很容易地表达。这种表达方式也是一种国际标准,像 Object pascal 语言Excel 等通用开发语言和工具中的日期类型也都采纳了这个标准。

其他语言的日期时间表达方式

大多语言和 TSL 的日期表达相近,也是从某天以来的天数,但是有的也存在基准时间点不同。

如 Excel 采用的日期标准和 TSL 语言相同。

而 MATLAB 的时间和 TSL 语言的时间相差一个常量,比天软大 693960相当于 MATLAB 的日期从公元前 1900 年开始。

SQLServer 中的日期和 TSL 语言相差 1基点为 1899 年 12 月 31 日开始。

有的语言采用从某个时间点以来发生的秒数作为日期时间的表达方式,这种表达除了基点 0 不同以外,一天的表达也相差了 24*3600 倍。还有的语言,日期不是采用数据方式。例如 JAVA 这种语言,就是采用“对象”来表达日期的。

String

在 TSL 语言中,字符串常量的表达方式是采用单引号或者双引号括起来。例如 123 表示数值为 123 的整数,而"123"则表示内容为 123 的的字符串,是两种不同的数据类型。由于字符串中可能会包括一些特殊的字符,例如回车换行以及引号,就采用\符进行转义,例如\r 表示回车字符,\n 表示换行字符。采用\符进行转义后,字符串里的\字符就得用\来表达了。例如'ab\r'd\r\n'表示的字符串内容是带回车换行结尾的 ab\r'd 字符串。

\转义的几个特殊字符为:

\表示\r 表示回车

\n 表示换行

\t 表示制表符 Tab

\0 表示 ASCII 值为 0 的字符

\跟其他任何字符都为该字符的本身

因此,'就是'"就是"

字符串中引号的另一种表示方法

TSL 语言还支持在字符串常量中表达'或者"的另外的方式,例如字符串常量表达如下:

'This"s a book.'

这个表示字符串的内容为 This's a book.

在字符串常量中,连续的两个与字符串开始的引号相同的引号可代表一个引号。

也就是说,"This""s a book."代表 This"s a book.,但是"This''s a book."则代表的是 This''s a book.

####号字符表达方式

有时候,程序中需要表达特殊的难以直接输入的 ASCII 符,例如笑脸符的 ASCII 值为 1要表达的时候就用#1 就可以表达了,而同样的,回车符的 ASCII 码值为 13可以用#13 来表达,而字符 0 的 ASCII 码值为 48则#48 可以表达数字字符 0。

#号字符表达方式可以和引号的字符串表达方式串起来表达字符串常量,例如:'The first line,Hello!'#13#10'The second line,Yeah!'#13#10'The End.'描述的字符串内容为:

The first line,Hello!

The seond line,Yeah!

The End.

其他计算机语言的字符串表达方式

事实上各种程序语言的字符串表达方式大同小异TSL 语言的字符串表达方式本身也是吸纳了多种程序开发语言的优点的情况下设计出来的。

C 语言的字符串常量表达方式为双引号将内容括起来,与 TSL 类似,特殊字符采用\转义但是更为复杂的是C 语言支持\0x0d 这样的转义,也就是说允许\后用 ASCII 值来转义0x 表示 16 进制0x0d 就是十进制的 13。

Pascal 语言的字符串常量是以单引号将内容括起来,并且没有转义符,但是支持连续的’号表达’号字符,这和 TSL 语言类似。与 TSL 类似的还有 Pascal 语言同样还支持#号字符表达方式,同样支持与 TSL 类似的字符串连结方式。

对于汇编语言而言,字符串常量既可以采用单引号括起来,还可以直接写字节码。由于存在一些特殊的计算机机器指令,因此许多语言的字符串都采用了以 ASCII 码 0 作为字符串的结尾。但由于这样的字符串无法表达 ASCII 码 0 的本身,所以也有的语言采用在字符串前边加入了字符串的长度。由于字符串长度的记载的字节的大小不同,假如是单个字节的,叫 ShortString只支持最长 255 的长度,而如果采用双字来作为长度,那么就可以表达最大长度为 4G 的字符串了。

对于 TSL 语言的字符串类型,采用的是有长度信息的字符串,所以可以支持存在 ASCII 码 0 的字符的字符串,并且理论上,支持长度为 4G 的串,实际上由于内存空间的限制,能表达的字符串会小很多。

ASCIIAmerican Standard Code for Information Interchange美国信息互换标准代码是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统。

参考:字符串运算章节。

内容
  • 字符串的表达
  • ANSI 字符串、Unicode 字符串与 UTF8 字符串
  • 字符串转义
  • 字符串的非转义表达%%
  • 字符串中#表达
字符串的表达

ATSL 语言中,采用的是 GBK 编码。

一般用引号(单引号或双引号)"xxx"表示一个字符串,为多字节字符串,如:

s:="Hello 天软!";

Unicode 字符串:

支持 L"xxx"方式表示一个 Unicode 字符串,也称宽字节字符串,如:

s:=L"Hello 天软!";

字符串内转义\u 可以表达 unicode 字符,\u 后默认为 16 进制的 Unicode 码,如:

s:=L"\u5929\u8F6F";

表示字符串 L"天软"

#####在 Unicode 字符串连接中被自动识别为 Unicode 码,默认为 10 进制,因而需要使用 0x 代表 10 进制,如:

s:=L"AA"#0x5929#0x8F6F

s 结果为 L"AA 天软"

UTF8 字符串:

支持 U"xxx"方式表示一个 UTF8 字符串,如:

s:=U"Hello 天软!";

也可以通过非转义方式描述:

如多字符字符串表示“我们”

s:=%% 我们%%;

宽字节字符串表示“我们”

s:=L%% 我们%%;

UTF8 字符串表示“我们”

s:=U%% 我们%%;
ANSI 字符串、Unicode 字符串与 UTF8 字符串

TSL 语言默认的字符串均是 ANSI 类型的。

但在语言内核已经支持了 UNICODE在字符串引号前加 L 就代表是一个 UNICODE 字符串。如 L”CCC”表明为 Unicode 的字符串”CCC”。

字符串内转义\u 可以表达 unicode 字符,\u 后默认为 16 进制的 UNicode 码,如 L"\u5929\u8F6F"表达 L"天软"

#在 Unicode 字符串连接中被自动识别为 Unciode 码,默认为 10 进制,因而需要用 0x 代表 16 进制,如 L""#0x5929#0x8F6F 表达 L"天软"

TSL 许多字符串相关函数都支持 Unicode函数版本一般是以 W 结尾,例如 InttoStrW(1)的结果是 L”1”。

WideString 函数可以把 ANSI 串转换为 UNICODE 串,例如 WideString(“AAA”)的结果是 L”AAA”。

类似的, String 函数可以把 Unicode 串转换为 MBCS 多字节 ANSI 串。在处理特殊的符号,以及一些特殊的中文时 UNICODE 具备一定的优势。

如果不特殊说明,缺省 TSL 字符串均是 ANSI 的。

Unicode 和 Ansi 字符串的运算返回以左操作数的类型为准,如"喜欢"+L"天软"的结果是"喜欢天软",L"喜欢"+"天软"的结果是 L"喜欢天软".

TSL 语言中大多数的算符、基础函数及基类都支持 Unicode 字符串。如字符串的拼接、比较等运算,字符串的截取与转换等基础模型,数据库、文件、邮件等处理基类等。

TSL 语言没有独立的 UTF8 字符串类型UTF8 字符串的本质就是一个字节串,也就是一种特殊的字符串。在绝大多数其他语言中也是如此。

但 TSL 语言对 UTF8 的常量串提供了支持,和 UNICODE 字符串的前导符 L 类似的UTF8 字符串的字符前导为 U例如 U"天软"表达的是 UTF8 串的天软。除此以外, TSL 还提供了丰富的函数支持 UTF8。

具体示例可参考FAQ字符串的表达

字符串转义

为了支持字符串包含一些特殊的字符,例如回车换行以及引号,就采用\符进行转义,例如\r 表示回车字符,\n 表示换行字符。

采用\符进行转义后,字符串里的\字符就得用\来表达了。例如'ab\r'd\r\n'表示的字符串内容是带回车换行结尾的 ab\r'd 字符串。

\转义的几个特殊字符为:

\表示\

\r 表示回车

\n 表示换行

\t 表示制表符 Tab

\x 之后跟 2 两个 16 进制字符,代表字符 ANSI 值,例如\x30 就是字符 0。

\u 之后跟 4 个 16 进制字符,对于 unicode 串而言表示 unicode 的值,对于 UTF8 串而言表示该 Unicode 值字符的 UTF8 串,对于 ansi 串而言代表两个字符。

\0 表示 ASCII 值为 0 的字符

\a 表示响铃符,等同于\x07

\b 表示回退符,等同于\x08

\f 表示换页符,等同于\x0c

\v 表示垂直制表符,等同于\x0b

\’表示’

\”表示”

\跟其他非转义字符的值都为该字符的本身,而不会报错。但由于转义符号未来可能会

扩展,所以除了已知的转义以外,不应利用此特性混淆字符串

字符串的非转义表达%%

如果有程序代码或者其他多行的字符串,转移表达描述起来相当复杂, TSL 使用%%符来支持不需要转义的原始串, %%支持 UNICODE 和 UTF-8 的 L 和 U 前导串。

说明:定义一个字符串,字符串中的转义字符无效。

定义:%%[标识符]<回车换行|回车|tab|空格>字符串原始串%%[标识符]

注:

1、通过标识符进行配对若没有则配对第一个%%。

2、字符串中的任何转义字符都是无效的比如%% a\r\nb%%它就是字符串'a\r\nb'。

例如:

%% \r\nABC%%

代表字符串\r\nABC无标识符字符串中包括""等转义符号

%%__

printf("%%smc")%%__

代表字符串 printf("%%smc"),其中__为标识符

L%% abcd%%

代表 UNICODE 字符串 L"abcd"

更多示例如:

范例 1%%的用法

a:=%%f1 ABCDEFG%%f1;

return a;

//返回字符串ABCDEFG

a:=%%a ABCDE\\FG%%a;

return a;

//返回字符串ABCDE\FG

范例 2%%与引号的区别

//表示字符串This's a book

s:=%%s1 This's a book %%s1;

q:='This\'s a book ';

return s=q;

//返回1

//路径中的应用

LJ:="C:\\Program Files\\Tinysoft\\Analyse.NETplug\\log";

LJ2:=%% C:\Program Files\Tinysoft\Analyse.NETplug\log%%;

Return LJ=LJ2;

//返回1

字符串中#表达

有时候,程序中需要表达特殊的难以直接输入的 ASCII 符TSL 支持以#号加 ASCII 码来描述该字符。

例如笑脸符的 ASCII 值为 1要表达的时候就用#1 就可以表达了,

而同样的,回车符的 ASCII 码值为 13可以用#13 来表达,而字符 0 的 ASCII 码值为 48则#48 可以表达

数字字符 0。

#号字符表达方式可以和引号的字符串表达方式串起来表达字符串常量。

例如:

'The first line,Hello!'#13#10'Thesecond line,Yeah!'#13#10' The End.'

描述的字符串内容为:

The first line,Hello!

The seond line,Yeah!

The End.

Binary

二进度制类型,二进制类型是用来存贮文件等二进制流的数据类型,这种类型主要用于象生成位图以及在 CGI 应用中返回位图以及文件。

取二进制串里的字符或者设置指定下标的字符,使用[]操作符,下标从 0 开始。

例如s[0]:="C";

此外,也可以直接设置 ASCII 值,例如 s[1]:=0x30; //char '0'

 C:=s[0]返回的是一个长度为1的字符串。

ArrayTableArray 数组类型

数组型,数组是程序中最常用的结构数据类型,用来描述一定数目的元素集合。数组的每个元素和下标相关联,根据下标指示数组中元素的位置。

数组中的元素可以是任意类型的数据;

一个数组在使用之前,首先必须初始化为数组,方式为:

数组名称 := Array()

数组是自扩张的,即数组的大小会随着设置下标而扩展,例如 a:=array();a[0]:-1;则 a 就自动扩张成了长度为 1 的数组。

数组的维度也是自扩张的,直接设置多维就可以产生多维的数组,例如 a:=array();a[0][0][0]:=0;则 a 变为三维数组。

TSL 的数组,有两种下标,一种为整数,一种为字符串。允许有字符型下标存在,其优点是可以直接描述一张数据表。例如 arrTable 是一个二维数组,我们可以设定其中的一个元素值如下:

arrTable[0]['日期1'] := '2002-04-15'

arrTable[0]['日期2'] := '2002-06-23'

这其中表示 arrTable 是一个二维数组,第一维为整数下标,第二维为字符串下标,这种类型的数组我们又称之为 TableArray在系统中象 Nday,NDay2 等函数以及 TGraph 的数据类型里依赖这种特殊的数组类型。

矩阵只是一种特殊的二维数字数组。

内容
  • 一维数组的理解
  • 多维数组的理解
  • 字符串也可以作为数组的下标
  • 数组的表达方式
  • TSL 语言的数组和其他语言相比的特性
一维数组的理解

可以假定有一种数据类型叫乒乓球,那么一筒乒乓球就叫乒乓球数组,而筒就是数组。假定有一种数据类型叫羽毛球,一筒羽毛球就是羽毛球数组。假定每个球按照位置有一个编号表示是第几个球,那么这个编号就叫做数组的下标,而指定下标的位置的球就叫做数组指定下标位置的的值。我们也把值称之为数组的项,或者元素。

多维数组的理解

我们再想想,假使有一种类型叫房间,那么一组房间是大厦,大厦就是房间的数组,而房间的编号就是下标。有的房间编号按照数字来的,例如 301302。这个房间编号其实是有讲究的301 其实是三楼 01 房。那么这样,我们就称之一栋大厦是一个房间的二维数组,第一维是层,是楼层的位置,第二维是房间在层中的位置。

我们同样可以认为有一种类型叫做层,那么一组层就是大厦。而层又是一组房间。所以二维数组我们依旧可以认为是一个一维数组,只是数组的值还是一个一维数组。我们用一个比较拗口的话来说,就是二维数组就是一个一维的一维数组。由此类推,假使一个小区是一个数组,小区里的每栋楼有编号,那么一个小区就可以算做是一维的大厦数组,既可以说是二维的楼层数组,也可以说是三维的房间数组。

字符串也可以作为数组的下标

我们再来假设另外一种情形,假如你是城市地名办的,要给街道命名,当然,你可以选择把城市分区按照一区,二区,三区,街道可以是一街,二街,叁街。但是倘若你进入了这样的一座城市,你还能找得到北吗?

地名都是用非数字来定义的,我们知道这名称是字符串类型,如果街道算一种数据类型,那么整个城市的街道就是一个街道数组,而下标就是街道的名称,值的内容就是街道。这就是字符串下标数组了。

同样的,回到我们之前理解得大厦和房间的关系,我们是否可以用户主的名字或者其他有特色的名字作为房间的标示呢?

古代的宅子喜欢叫张宅,李府的,现在的餐馆的房号也喜欢叫什么井冈山,阿里山什么的,一看见高山流水就知道是洗手间。

字符串的命名模式,在很多情况下,显然是用更为方便,含义也更明确,只是由于位置不明确,所以查找起来比较麻烦,要一个个去看,而无法像数字号码一样可以直接定位。但是,这正是计算机的强处,在 TSL 中,字符串作为下标可以直接根据下标引用到其中的内容,所以既明确,往往由于直接定位而省略了查询的时间。例如在计算机中,假如采用数字下标,一旦要定位王二麻子的家,一种方法是先查王二麻子的家是在什么房号,再去根据房号取内容,又或者采用一间一间的房间查,看哪家的主人叫王二麻子,这显然不如直接用字符串”王二麻子”作为数组下标直接取内容来得效率高。

数组的表达方式

在 TSL 中表达一个数组是很简单的array(2,3,5,7,11)就是一个数组,里边有 5 个整数数据元素,分别为 2、3、5、7、11也就是说数组的表达方式就是在在 array()中将数组中的项使用逗号分隔就可以了。采用这种数组表达方式TSL 中的默认的数组下标从 0 开始,也就是说值 2 的下标为 0值 3 的下标为 1依次类推值 11 的下标为 4。由于数组中的值都是整数我们称 array(2,3,5,7,11)这样的数组为一维整数数组,这种类型我们可以描述为 array of Integer。

如果 array()的中间没有数组的项,这就是空数组了,空数组就是项数为 0 的数组。这个项数在计算机中有一个专有名词,叫做数组长度,英语表达一般为 length。例如我们也可以说 array(2,3,5,7,11)是一个数组长度为 5 的一维整数数组。

TSL 语言的数组支持数组中的不同项的数据类型不相同,例如 array(2,3,”Hello”)就是一个既包括整数又包括字符串的混合类型数组。

采用上述方式无法表达字符串下标的数组,字符串下标的数组的表达方式为 array(“身高”:1.75,”体重”:85,”姓名”:”李四”),和 0 下标的数字下标不同的是,在项前加入了下标字符串和冒号。

事实上,数字下标也可以采用与字符串下标数组类似的表达方式,例如 array(0:2,1:3,2:5,3:7,4:11)等同于 array(2,3,5,7,11)。对于 array(2,3,5,7,11)等于一个数组特例,即当数组下标为以 0 开始的连续序列时,数组下标以及冒号可以被省略。

基于前边讲到的多维数组的理解,多维数组也就不难表达了。例如一个二维数组可理解为一个”数组项的内容为一维数组”的一维的数组。那么 array(array(0,1,2,3,4),array(3,4,5,6,7))就可以描述一个两行五列的二维的数组:

其表达的数组的内容为:

0 1 2 3 4

3 4 5 6 7

由于这样表达稍嫌繁杂,当数组为 0 开始的连续数字下标的时候,其项如果是数组,该项的数组的 array 可以被省略也就是说在这种情况TSL 语言允许省略掉除第一维以外其他维的 array(),也就是说:

Array((0,1,2,3,4),(3,4,5,6,7))等同于上面的 array(array(0,1,2,3,4), array(3,4,5,6,7))。

TSL 的数组中不仅仅允许使用常量,还允许进行计算,例如 array(1+3,2+5)和 array(4,7)的结果是相同的。

Array(Today(),DayOfWeek(Today()))初始化一个包括当天的日期以及当天星期几的数组。

TSL 语言的数组和其他语言相比的特性

首先,绝大多数的语言的数组都不能用字符串类型做下标,只有少数语言支持这种字符串为下标的数组。

但是由于字符串下标非常方便,所以其他的语言有的也提供了一些类似的替代功能,对于 C++,使用标准模板类 STL 中的 MAP 类,可以达到类似于 TSL 的字符串下标的效果。

此外,绝大多数语言的数组的项是必需相同的,不能有混合型的数组。

再次TSL 的数组的维数以及长度是可以任意的,而很多语言的数组在使用之前需要预先确定。而 TSL 的数组可以在使用的过程中自动扩展维数或者长度。

在后续的章节中,通过对 TSL 语言的数组的应用的学习,必将理解其功能的强大性。

TMatrix

矩阵类型,特指二维实数数组,即 array of array of real。

Matrix

特殊的二维数组结构,第一维为整数下标,且从 0 开始,第二维可为字符串或者整数,该类型主要是内存紧凑,在处理特大数据集合的时候可以节省内存和加速建立集合。

与其相关的函数有IfMatrix 判定类型是否为 Matrix 类型CreateMatrix 来创建 Matrix 类型,对 Matrix 类型而言,一样可以用 matrix1[0]["字段1"]这样来访问指定行列的数据,但是不能用 matrix1[0]来访问其第一行。

Matrix 类型非常类似于 Array 类型,但是对于许多操作符,仅仅只支持 Array 类型,可以使用 MatrixToArray 函数进行转换。

NIL

NIL 类型是一种很特殊的类型,在 TSL 语言中NIL 往往用来判断数据是否存在。与 NIL 相关的函数有 IfNil 用来判定是否为 NIL。

当然,和 DLL 程序的外部交互的时候NIL 是有用的,例如外部要一个空指针,和 COMWindows 开发中的一种组件开发模式Component Object Model组件对象模型交互的时候也是有意义的。

绝大多数的语言都没有 NIL 类型,但是有 NIL 值,例如 C 语言中的 NULLPASCAL 中的 NIL。但那些都只是空指针的概念。TSL 中的 NIL 更接近于 COM 中的 Unassigned。

在 TSL 中,在判定数组中某个下标中是否有值会非常有用。这个数组概念有些类似于矩阵,矩阵中有非完全矩阵的概念。数组的具体知识在之后会讲到。

在 2018 年的 TSL 版本中,允许 NIL 和数字进行运算,以支持非完全矩阵的计算,而以前此类计算会导致异常。

TGraph

图形型TGraph 是 TSL 特有的一种数据类型它是由特定终端程序来解释和显示Graph()函数是它对应的数据生成函数。TGraph 在特定终端显示出来的是一种图形,图形的形状、颜色、大小等等完全由函数的参数来指定。

TGraphGroup

图形组型,同 TGraph 数据类型相似TGraphGroup 也是 TSL 中特有的数据类型。它是多个 TGraph 数据的集合。对应的图形生成函数是 GraphGroup()。

TExpression

表达式型,表达式是 TSL 中一种特定的数据类型,它是一种在函数执行中被动态求值的变量,你可以通过运算符@和&来得到一个表达式。如果要对一个表达式求值,需要调用函数 EVAL()。

TFUNCTION

函数型,存贮一个函数指针,一般用 ThisFunction 来获取。

函数型可以用 Call 来调用。

参见Call

TCLASS

类型,存贮一个 TSL 类指针,一般用 Class 来获取,获取方法为 Class(类名)。

TCLASS 型数据可以用.操作符来调用类方法。

例如 Class(ClassA).MethodA(A,B);

ComObj

对象型,对象型是 TSL 中的一种 Com 对象数据类型,用这种类型可以操作各种 Com 对象,如 Excel 以及 Word 对象。

对象的操作方法:

访问属性Value:=ComObjVariant.PropertyName;

对属性设置值ComObjVariant.PropertyName:=Value;

访问带参数的属性Value:=ComObjVariant.PropertyName(Param1,Param2...);

对带参数的属性设置值ComObjVariant.PropertyName(Param1,Param2...):=Value;

访问方法ComObjVariant.MethodName(Param1,Param2...);

如果对象的属性或者方法返回的仍然是对象,支持嵌套调用:

ComObjVariant.<PropName|MethodName>[(P1,P2...)]. <PropName|MethodName>[(P1,P2...)]...

NIL 和 COM 的兼容性:由于 Excel,Matlab,S-PLUS 等多种统计软件对空类型的支持不尽相同,导致出现兼容性问题,

无法用统一的转换类型来解决,现在通过增加一个 COM 组件属性 NilTrans 来解决此问题,由用户自由选择转换的方法

假定天软的 COM 组件实例为 ts

具体操作如下:

设置 ts.NilTrans 属性为下列值(默认为 0

 0: 转换为Unassigned

 1: 转换为Empty

 2: 转换为NULL

 3: 转换为NaN

 4: 转换为整数0

 5: 转换为浮点0

 6: 转换为空字符串

 7: 转换为'NIL'字符串

 8: 转换为'NULL'字符串

 9: 转换为'-'字符串

10: 转换为'/'字符串

其他值: 转换为 Unassigned

BTW:

假如用户在其他的本地应用中与外部 COM 打交道,例如调用 EXCEL那么用户可以在 TSL 中使用

SetSysParam('NilTrans',N);//N 为 0-10 的上述值

的方式来调整 NIL 对 COM 数据类型转换的行为。

参考GetOleObject,CreateComObject,ifObj,SysGetPidOfCom

内容
  • COM 方法的调用例子
COM 方法的调用例子

COM 的调用和 TSL 的对象的调用是没有差异的,也非常类似于 PASCAL 以及 VB 等对 COM 对象的调用。每个 COM 对象方法或者属性的调用要么返回一个 TSL 基本类型如数组数字字符串NIL 等,要么返回一个 COM 对象。

例如:

GetOleObject("Excel.Application",0,Obj);

Obj.WorkBooks.Open("C:\1.xls");

上边的代码 Obj.WorkBooks 隐含着返回了一个 WorkBooks 对象

TSLObj

TSL 对象型TSL 对象型是 TSL 语言中特有的对象数据类型,可以由扩展的 TSL 语言插件注册该对象类型。也可以使用 TSL 语言编写 TSL 类,这种对象类型不同于操作系统的对象类型,但是操作的方法与 ComObj 类似。

创建该种对象的方法是 CreateObject。相关对象的使用方法见TSL 内置对象使用大全

参见函数CreateObjectifObj

常量与变量

变量是指在某个函数的运行过程中其值可以发生改变的标识符。

在 TSL 中的变量是不用初始化和预先声明的,你可以把某一个变量赋值为一种数据类型后,在接下来的语句中再改为另一种数据类型而不必做任何的声明,这和一般的高级语言不同。

变量类型为弱类型,变量类型与运行时相关。

TSL 语言标示符大小写无关,未初始化的变量为整数 0。

默认情况下所有的函数参数均为变参,请注意对入口参数的保护。

可以通过编译选项控制变量需要预先申明具体用法可参考Explicit 变量声明的编译选项

注:面向对象 TSL 的对象成员变量初始值为 nil因成员变量必定预先申明与此规则并无相悖。

一个变量的赋值语句为:

标识符:=任何数据类型的常数或是另一个变量或是一个函数返回值;

例如:

  dToday := Date();

变量 dToday 通过 Date()函数的返回,被赋值为一个 TDateTime 类型的数据。

常量即同 C 语言中的常量,它是一个固定值,在程序执行期间不会改变。

截止到 2023-07 月份前的 TSL 版中TSL 中没有特定的常量标识符,即不支持通过 const 来定义一个常量标识符。

而在 2023-07 月份之后,天软新一代 TSL 语言中支持常量的使用,并提供了一般常与运行时常量。

内容

  • 全局变量
  • 常量
  • GetEstSize

全局变量

全局变量时指在当前运行的任务中,无论在哪个函数中均可以访问。

全局变量必需预先声明。

例如 global x,y,z;声明了三个全局变量 X,Y,Z。在每个引用全局变量的函数中均需要采用 global 进行声明不进行声明TSL 会假设其为局部变量。

例如:

Begin


global x,y;


  x:=100;

  testglobal();

End;

Function testglobal();

Begin


global x;


  echo x;

End;

//使用了全局变量之后以上的testglobal函数会打印出100。

常量

常量是一个固定值,在程序执行期间不会被改变。

TSL 中支持常量、常量成员、运行时常量。

其中,常量成员是指类中的成员变量也可以定义成常量成员;

运行时常量又叫编译时常量,它的初始化与一般常量不同,是在程序运行时才被确定,但仍有初始化后不会被改变的特性。

内容
  • 常量及常量成员的定义与初始化
  • 运行时常量的定义与初始化
  • 常量与运行时常量的对比
  • 常量的产生对其它功能的扩展帮助
常量及常量成员的定义与初始化

常量与常量成员的赋值符用的是“=”

其定义与初始化:

Const 标识符 = 基础数据类型的常数或常量参与的运算结果;

如下:

【常量定义】

Const a="Hello";

Const b=a+"Tinysoft";

【常量成员定义】

Type C=class

  Const a="Hello"; //可以在声明中进行定义初始化

  Const b=a+"Tinysoft";

  C=a+b+"from TSL";

End;

常量定义与常量成员定义的值可以是一个常数也可以是常量参与的计算。

其中,常数的数据类型及运算符如下:

数据类型整数、实数、字符串、nil、数组(包括 FMarray)、其它常量、常量成员。

运算符:如+、$、-、*、/、等基础运算符,具体如下图所示。

常量成员的初始化:有别于类的一般属性只能在构造函数中进行初始化,常量成员可以在成员声明这一层直接对该常量进行初始化。

作用与意义:

1 天软的变量在通过参数传入函数后,是允许对该变量进行修改的,所以,为了避免某些需要常量意义的变量不被修改,可以定义这样子的一个常量进行,它有不能被修改的特性。

2 常量在使用过程中比一般的变量的性能更高,尤其是在它存储的值较大的情况下。

运行时常量的定义与初始化

与常量的差异是运行时常量定义的内容是在每次计算时确定,其值的计算没有约束,支持任意类型、任意运算。

运行时常量的赋值符号为”:=”

其定义与初始化:

Const 标识符 := 任何数据类型的常数或任意表达式的返回值;

例如:

Const a:=1;

C:=100;

Const b:=C+now();

则此时的 b 只有在第一次运行时才能知道它的具体值。

特性说明:

1 初始化时允许任意表达式计算得到(即可调用任意函数、变量做任意运算)

2 函数每次运行都重新计算

3 和普通常量一样不允许被修改

4 普通常量又称之为编译时常量

5 未做类成员运行时常量支持

常量与运行时常量的对比
特点 常量定义 运行时常量定义 变量定义
声明关键字 Const Const存在编译选项时用var
初始化赋值符 = := :=
初始化数据 常量或常量参与的计算 任意表达式 任意表达式
初始化计算种类 类型与算符有限制,具体可参考:计算种类 无约束 无约束
初始化生效 初始化时确认,是预知的 第一次运行时确认 运行时确认
初始化后是否可被修改 不能被修改 不能被修改 能被修改
是否支持类成员 支持 不支持 支持
是否支持声明中初始化类成员 支持 不支持 不支持
运算效率 存储数据较大时效率相对较高 存储数据较大时效率相对较高 一般
定义初始化举例 Const a="Hello"; Const a:=100+now(); a:=100+now();
常量的产生对其它功能的扩展帮助

1 允许 External 常量而非仅支持字符串,对于跨平台的动态库有帮助。

2 Com 性能提升

GetEstSize

范例

  t:=rand(10,10);

  return GetEstSize(t);

返回整数8800

注意:其结果是一个估算值,当变量占用空间较小时,可能会返回 0属于正常现象。

弱类型语言与变量类型运行时相关

对于 TSL 语言而言,使用一个变量不需要预先进行变量类型申明,一个变量既可以是数字,也可以是字符串,可以先是一个类型,运行后又是另外一个类型。

例如:

A:="My test string"; //先给 A 赋值字符串

A:=length(A); //取 A 的长度赋予 AA 变成了整数 14函数 length 可以得到字符串 A 的长度,关于 TSL 语言的函数参阅后面章节

A:=A*10.0; //A 的类型运算后变成了实数 140.0

A:=array(A); //A 的类型变成了数组,内容为 array(140.0)

这种变量类型可以改变的特性,我们称之为变量类型的运行时相关,也就是说,变量类型只有在运行到具体位置的时候才可以被明确,这种特性在计算机术语里也可以称之为 TSL 语言是弱类型的语言。

内容

  • TSL 语言变量的初始值
  • 弱类型与强类型
  • 数据类型转换与算符重载
  • Explicit 变量声明的编译选项

TSL 语言变量的初始值

在 TSL 语言中,假定引用一个未赋初始值的变量,该变量的值会被系统初始成整数 0对于数组的项或者类成员变量未初始的值则为 NIL。

弱类型与强类型

弱类型是相对于强类型语言而言的,绝大多数编译型语言是强类型语言,其类型在变量申明的时候就已经确定了。

在强类型语言中,一般来说,变量必须预先申明,例如:

在 C 里:

int I;//申明 I 为整数类型

I=0;//赋值语句C 语言的=和 TSL 的:=意义是一样的

先得申明 I 变量为整数,才可以使用 I。

而且这样是不正确的:

int I;

I="1234";

这样 C 语言会报告类型不匹配的错误,因为无法把一个字符串类型赋给一个整数类型的变量,这就是强类型语言,而且,无论在哪里,只要使用 I都可以知道 I 是整数类型。

数据类型转换与算符重载

但是,对于某些其他语言而言,刚才这样的语句却是可行的。由于 I 是整数类型有的语言会自动转换字符串”1234”为 1234 并赋予 I。这种语言事实上还是强类型语言只是做了强制类型转换这【类似】于语言中的另外一个概念叫算符重载,也就是说,可以根据类型信息决定运行的行为(算符重载在本质上和这个是一样的,但是从很多“专业”的意义上,两者是不同概念,实质上,一个是编译器语言内核做的算符重载,另外一个则是语言应用层面上的重载,实质是相同的)。拥有这种特性的语言有点意思,写起代码来很优雅,尤其是很多计算机编程的初学者都喜欢这种特性,但是对于一个开发经验丰富的人而言,这种特性带来的坏处往往超过了其带来的便利性,我们来看一个 TSL 语言的例子:

A:="1234";

B:=1;

C:=B+StrToInt(A); //StrToInt 是一个把字符串转换成为整数的函数,我们先不去理解 TSL 的函数的表达方式,函数会在后续章节中讲解

在 TSL 中,我们很明确地先把字符串 A 用相关函数转换成为整数再和 B 相加,这样我们很清楚 C 的结果是 1+1234=1235。

假定是支持数据自动转换的语言,这样写就可以了:

A:="1234";

B:=1;

C:=B+ A;

编译器会自动识别 B 为整数,整数和后边的类型相加,自动把后边的数据类型转换成为整数,这样我们可以称之为语言内核已经重载了+运算符使得自动执行了将第二操作数自动转换成为和第一个操作数相同的类型也就是自动把字符串”1234”自动转换成为和整数 1 一样的类型的功能。

以上代码看起来显然比没有自动类型转换的看起来要简单和优雅,但是我们再看下一个代码:

A:="1234";

B:=1;

C:= A+B;

这个 C 的结果会是什么呢?由于 A 是字符串,所以先要把 B 的结果 1 先变成字符串"1"再和 A 相加,结果就变成了"12341"。

这样就出现了很意想不到的效果了A+B 的结果与 B+A 的结果是完全不相同的,假定开发人员写的时候稍有不注意,这类的错误是非常容易犯下并难以查找的。

正因为数据类型的自动转换和算符重载功能是一把双刃剑,因此 TSL 语言并不支持数据类型的自动转换和算符重载。所有算符重载的功能都可以使用不重载的方法来实现,仅仅只是代码看起来繁杂一点,但是其意义的明确带来的好处完全可以抵消代码繁杂的坏处。

2016 年下半年的 Object TSL 已经支持对象的算符重载,以支持 TSL 的对象利用标准算符进行计算。

Explicit 变量声明的编译选项

无需提前声明就可以直接使用的特性使得 TSL 语言非常容易使用但是这种便捷的特性同时也会带来负面的影响。当开发大型程序的时候用户比较容易在写程序的时候遇到写错变量名的情况由于在程序的语法而言并不是一种错误只是运行的结果和预期不一致这种错误很难查找TSL 语言提供了另外一个选择给用户,即允许用户选择必需先申明后使用的模式。

用户在程序代码前加上编译选项{$Explicit+}就可以让出现在这个编译选项之后的所有代码(无论是否在一个函数内,只要是同一个源代码文件内)都会遵循先声明后使用的模式,要关闭这个选项则使用{$Explicit-}。

TSL 采用 Var 作为前缀来声明变量,具体使用方法为:

Var varName[:Type][:=InitExpression];

类型可以省略,即便声明,目前 TSL 语言也不采用该类型信息,当需要赋初值,则使用在变量后加赋值语句。

如果有多个变量同时声明,则采用:

Var varName1,varName2[:Type];

例如:

  D:=1;

  {$Explicit+}

  Var A,B; //同时定义两个变量A和B

  Var E:=D+10; //给变量E赋初始值

  A:=1;

  B:=2;

  C:=3; //该变量未预先声明

系统会提示 C:=3 这一行“变量未申明”。我们来理解下上面的代码D:=1;不会出错,因为默认是无需变量先声明的,但是当使用了{$Explicit+}之后,所有的变量必需经过声明,所以 C:=3 就会出错了。

是否需要预先声明变量完全是个双刃剑,可以获得方便的同时也会带来副作用,读者可以自己选择自己的编程风格。事实上,即便写大程序没有选择变量声明的模式,当出现变量名错误问题,定位到问题也比较容易,因为 TSL 语言提供了很便捷的辅助调试功能,例如有输出当前运行环境中所有变量的功能,而在调试器中也可以看到所有存在的变量名,一旦有写错了变量名的情况,开发人员可以很容易发现。

运算符

运算符是用于运算的符号,例如+号A+B 表示对 A 和 B 进行加法运算,参与运算的元素个数为几,就叫几元运算符,大多数运算符号都为二元运算符号,也就是需要两个元素参与运算,例如加法需要两个元素参与。一元运算符号有++,--,&,@,Not 等,他们只有一个参与运算的元素。

事实上,+和-也可以是一元运算符。例如 1+-2 的结果是-1因为-作为一元运算符的时候只是负号,相当于 0-1-+2 的结果是-1因为+作为一元运算符号的时候只是表示正号。但是值得注意的是 1++2 是不可被支持的,虽然按理结果为 3但是由于++也是算符,这样就产生了歧义,因此必须采用 1+(+2)才是正确的同理1--2 也是不成立的。

三元运算符在 TSL 中只有一个,就是?:表达式a>b?1:2当前的条件为真时候返回:前的表达式值,否则返回后边的。

TSL 的算符有的存在.前导,例如 .&,.>等等,需要注意的是,由于.是实数的小数部的分割符,所以当运算常量数字的时候,要格外小心,否则非常容易出现意想不到的结果。例如 1.&2 会出错,因为系统将 1.理解成为实数 1类似于(1.)&2因此需要用户写成 1 .& 2也就是在 1 和.&之间使用空格分隔。

此外,还有一些带.的算符可能在读者看起来毫无存在的必要,例如既然存在=算符,.=的计算也和=一样,为什么还要.=呢?这是有原因的,因为=和.=在对数组类型运算的时候表现将不一致,但是这些差异我们在这一节将不做详细解释,等到后边的矩阵计算的相关章节再做详细说明。

内容

  • 赋值运算
  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 三目运算符
  • 位运算符
  • []运算符
  • 表达式运算符
  • 对象访问符
  • 矩阵运算符
  • 集合运算符
  • 过滤集运算符
  • 运算赋值运算符
  • With 设值运算符
  • 算符优先级
  • ?.模式

赋值运算

天软中赋值运算符为:=

例如: a:=1;

注: MATLAB 和 C 语言用户,注意=号为关系判断符,而非赋值

内容
  • 多参数赋值运算
多参数赋值运算

说明:将数组中的值依次赋值给对应位置的变量

语法:[变量 1,变量 2,...]:=array(值 1,值 2,...);

注意:

1 右边的数组行标(即第一维)必须是自然数字下标,支持多维数组。

2 左边变量必须从第一个开始,依次赋值,前面的变量不能为空。当变量名存在同名时,后面的会覆盖前面的。

3 该种用法中,变量个数只有一个时,后面的逗号不能省,即最短写法为[r1,]:=array(1)

4 右边的值必须是一个数组,可以是多维数组

5 左边的变量数大于右边的行数时,无对应值的变量值为 nil。左边的变量个数可以少于右边数组的行数。

6 支持在函数传参中使用,具体用法可参考范例 05。

范例:

范例 01一般用法

//将数组中每个值都赋值给对应位置变量

[r1,r2]:=array(1,3,5,7,9);

return r1+r2;//返回4其中r1的值为1r2的值为3。

范例 02变量最少的用法

//将数组第一个值赋值给变量

[re,]:=array(1,2,3,4);

return re;//返回1

范例 03变量个数多于数组长度的示例

//数组中只有一个值

[r1,r2]:=array(1);

return r2;//返回nil

范例 04二维数组

[r1,r2]:=array((1,2),(3,4));

return r2;//返回array(3,4)

范例 05函数传参

说明:调用函数时,当参数是表达式时,会先根据参数的位置依次执行表达式,然后将表达式的值传入到被调函数中。

比如存在函数 test(a,b,c),具体实现如下,在调用时,执行如下:

return test(e:=3,[f,g]:=array(1,2),g);

其传参数可理解为:先将 3 赋值给变量 e再将 e 的值作为函数的第一个参数传入,再执行表达式[f,g]:=array(1,2),分别给变量 f、g 赋值并将变量 f[f,g]中的第一个值)的值作为函数的第二个参数传入,最后一步将变量 g 的值作为函数的第三个参数传入;所以执行结果为 3+1+2=6。

而当执行 return test(e:=3,[f,g]:=array(1,2))会返回 4。原因是没有传入第三个参数因此结果为 3+1+nil=4。(nil 参与运算不报错的情况下,若报错模式则会引发程序报错)。

注:此种用法并不常用。

//test函数内容

function test(a,b,c)

begin

  return a+b+c;

end

范例 06实用场景当我们执行某个程序之后想要返回多个指标值可以采用这种方式进行处理会更快便捷。比如在数据库交互过程中做一个数据提取的操作的时候我既希望可以返回提取的数据结果集又希望在提取失败时返回具体失败的信息方便查看失败的原因。那此时就希望能一次返回两个结果一个代表数据库操作是否成功的 Bool 类型结果,另一个代表的是提取的结果集或报错信息。那通过数组绑定变量的方式就可以很好地读取到对应的信息值,大致过程如下所示:

//被调函数

function add(a,b)

begin

  if not (ifreal(a) and ifreal(b)) then

    return array(1,"传入参数不为实数");

  return array(0,a+b);

end

调用方式如下:

[error,re]:=add(3,4); //error变量取到的是计算结果是否异常re代表的是执行结果或报错信息

if error then begin

...//异常处理

end

...//获取到的正常值继续进行计算

算术运算符

算术运算符是最常用的运算符,用于从事算术运算,除了四则运算+-*/以外TSL 还支持求余,整除,自加一,自减一等。

运算符 运算 运算对象 结果类型 例子
+ 加,正号,可为一元或者二元运算符 整型、实型、字符串 只要有一个运算对象是实型,结果就是实型,如果全部的运算对象都是整型并且运算不是除法,则结果为整型,若运算是除法或者左除,则结果是实型 2+1结果为3 "222"+"888"结果为"222888"
- 减,负号,可为一元或者二元运算符 整型、实型 2-1结果为1
* 整型、实型 2*2结果为4
/ 整型、实型 2/4结果为0.5 注意:在某些语言中,整数常量相除的结果为整数,如果要结果为实数需要其中一个参与运算的元素为实数
\ 左除 整型、 实型 3\2的结果为0.666666… 左除这种运算符只有少数语言支持,大部分语言并不支持
%,Mod 取余 整型 整型 例如4 % 3的结果为1
Div 除取整 整型、实型 整型 例如7 div 3的结果为2
++ 自加1一元运算符 整型、实型 整型、实型 A++相当于A:=A+1; 因此,当 A:=2; A++; 结果A为3 A++的本身的返回值为原始值为2。 ++还有另外一种用法,为++A,对于A而言结果依旧为3但是++A本身的返回值为运算后的值亦为3
-- 自减1一元运算符 整型、实型 整型、实型 A--相当于A:=A-1; 因此,当 A:=2; A--; 结果A为1 的本身的返回值为原始值为2. --还有另外一种用法为—A对于A而言的结果依旧为1但是—A本身的返回值为运算后的值亦为1
! 求倒数,一元运算符 整型、实型 实型 !A相当于1/A
^ 求幂 整型、实型 实数 2^3等于2的3次方为8
~ 求对数 整型、实型 实数 8~2等于8对2求对数为3

关系运算符

关系运算符主要是用于进行逻辑判断的,返回值为逻辑真假值。

基础判断符如=,<>,<,>,<=,>=,like

支持矩阵判断符如.=,.<>,.<,.>,.<=,.>=

在=,<>,<,>,<=,>=等运算符都可以对字符串类型进行判断,判断是以字符串中的字符的 ASCII 码值的大小为原则的,比较原则:从第一个字符开始比,相同则比下一个字符,如果两个字符串不一样长,且短串长度部分都相等,则长串大。此外,大小写的 ASCII 码值是不同的,小写的码值大,因此字符串比较是大小写相关的,例如"A"<"a"为真。

运算符 运算 运算对象 例子
=,.= 等于 简单类型 1=2的结果为假 注意:在有些语言中,=被用于赋值符,这样等于判断符就使用==
<>,.<> 不等于 简单类型 1<>2的结果为真 注意:在有些语言中,采用!=作为不等于的判断符
<,.< 小于 简单类型 1<2的结果为真
>,.> 大于 简单类型 1>2的结果为假 "B">"A"结果为真
<=,.<= 小于等于 简单类型 1<=2的结果为真
>=,.>= 大于等于 简单类型 1>=2的结果为假
LIKE 模式匹配判断 字符串 "2009-1-1" like "\\d+-\\d+-\\d+"为真 在TSL中Like符和SQL的Like的含义不同TSL的Like的右元素为正则表达式左边运算元素为匹配串结果是左边串是否匹配右边的正则表达式关于正则表达式可以参阅的相关正则表达式的章节

逻辑运算符

逻辑运算符就是对真假的 Boolean 类型进行运算的算符。

运算符 运算 运算对象 结果类型 例子
Not,.!! 逻辑非,一元运算符 布尔型 布尔型 逻辑非,对真运算则为假,对假运算则为真 Not 1的结果为0 在C语言或者类C语言中Not的表达为!
And,.&& 逻辑与 布尔型 布尔型 逻辑与,真与真与为真,真与假或者假与假与都为假 1 and 2结果为1 1 and 0结果为0 在C语言或者类C语言中And的表达为&&
Or,.|| 逻辑或 布尔型 布尔型 逻辑或,只要一个为真,运算结果就为真 1 or 2 结果为1 1 or 0结果为1 0 or 0结果为0 在C语言或者类C语言 中Or的表达为II

TSL 在很多方面和 Pascal 语言比较类似,但是在 Pascal 语言中Not,And,Or 同时可以作为位运算符,由于 TSL 为弱类型语言为了避免冲突TSL 取消了这一特性,位运算有相应的一组算符和函数。

Not 作为结合算符的支持

判断别算法 In,SQLIn,Like,Is 没有对应的反向含义算符,如果需要判定非则需要使用 not( a in b)这样的模式。

为了更简便书写和可读性,特别在 2025/8 月版本中增加了 not 和这四个算法的结合。

即 not in,not sqlin,not like,not is

如 a not in b 相对于 not (a in b)的可读性更好

三目运算符

内容
  • ?:判断求值三元运算符
  • 支持矩阵的.?:表达式
?:判断求值三元运算符

语法 1Condition?TrueResult:FalseResult

功能:?前的逻辑表达式的运算结果如果为真,则?表达式的结果为?后的值,否则为:后的值。

具体使用案例:

1>2?"大于":"不大于"

该运算的结果是"不大于"

2>1?"大于":"不大于"

该运算的结果是"大于"

2>1?1:0

该运算的结果是 1

1>2?1:0

该运算的结果是 0

容易被忽略的使用方法:

2?1:0

该运算的结果为 1

0?1:0

该运算的结果为 0

以上正确的原因是由于 2 的本身是非 0自身就可以成为逻辑表达式结果为真而 0 为假。

因此,当遇到 a?1:0 的时候,实际上是判断 a 是否非 0 非 nil

内容
  • ?:省略真表达式的三元运算符的特殊用法
?:省略真表达式的三元运算符的特殊用法

语法 2Condition?:FalseResult

说明:若 Condition 为真,则返回 Condition若 Condition 为假,则返回 FalseResult。

在计算中,经常会出现有 NIL 值NIL 值的产生原因是多种多样的,例如多表的 SELECT JOIN 查询产生(我们姑且先不去理解什么是 SELECT JOIN当产生 NIL 值以后NIL 无法和其他数据类型进行运算,会产生错误。用户可能会希望在计算的时候利用 0 或者空字符串来替代掉 NIL使得计算可以继续。

例如:

Aexp+1由于 Aexp 可能是 NIL 会出错,那么用户可能会采用如下来替代:

(Aexp?Aexp:0)+1

有时候 Aexp 计算的表达式很长,这样写起来就会很不方便,运算的时候效率也会比较低,因为 Aexp 可能需要计算两次在这种特殊用途中TSL 语言支持省略掉真表达式,缺省认为为真的时候的值就是?之前表达式的本身的计算结果,这种写法就可以缩略为:

(Aexp?:0)+1

示例计算一个序列中各元素的和nil 值用 0 替代。

a:=array(1,2,3,nil,100);

s:=0;

for i,v in a do

 s+=v?:0;

return s;

返回结果为106

支持矩阵的.?:表达式

语法 1ConditionMatrix.?TrueResult:FalseResult

说明对数组进行判断取值TrueResult 和 FalseResult 既可以是数组类,也可以是标量。

语法 2Condition.?:FalseResult

说明:为真的单元格采用条件的自身单元格,为假的单元格用 FalseResult 或者 FalseResult 内的单元格替代。

支持矩阵,可以对 fmarray 或 array 的单元格进行判别,组装出对应的矩阵,这样的做法可以避免有些需要循环判别的矩阵操作。对于全数字而言,以前实现需要使用 bool 矩阵和值矩阵相乘后相加来完成:如(a.>3) * a1 + (a.<=3)*a2或者采用 mfind 得到的数组取子矩阵来操作。

新的支持会使得这种操作极为便捷,也符合思维定式

例如 a:=array(1,2,3,4,5);

b:=a.>3 .? array(2,3,4,5,6) : array(3,4,5,6,7)

b 的结果为array(3,4,5,5,6)

即,对集合 a 中每个元素的集进行判断取值,然后得到对应的结果返回到结果集中。

同样,这种方式也支持 FMArray。

但是需要注意,当 f.?A:B 中A 与 B 都是 FMArray 时,两者的维度与大小必须是一致的。

当 A 与 B 的单元数据类型不一致时,也符合 FMArray 矩阵合并时对单元格数据类型的包容处理规则。

例如:将矩阵 f1 中不大于 3 的值用矩阵 f2 中的元素替换

f1:=fmarray1,2],[0,4],[1,2],[5,6;

f2:=fmarray100,10],[200,20],[300,30],[400,40;

b:=f1.>3 .? f1:f2;

return b;

结果:

例如:将整型矩阵中不符合条件的值用浮点数 10.5 替换

f1:=fmarray1,2],[0,4],[1,2],[5,6;

b:=f1.>3 .? f1:10.5;

return b;

返回的矩阵中,所有单元格类型都为浮点数:

注:即便判断中没有出现符合条件的单元,数据类型不同时也会做包容处理。

位运算符

位运算符顾名思义,就是按照位来进行运算,在目前计算机的内部,都是以二进制方式对数据进行存贮,例如十进制 4 的二进制的写法则为 0b100十进制 5 的二进制写法为 0b101位运算的规则

1 与 1 为 1否则为 0

0 或 0 为 0否则为 1

非:非 1 为 0非 0 为 1

异或1 异或 0 为 1否则为 0

按照与的规则4 和 5 与的结果为 0b100结果为十进制的 4。

下述所说的整数为 32 位整数或 64 位整数。

运算符 运算 运算对象 结果类型 例子
.& 位与 整数 整数 位与例如3 .& 1 的结果13 .& 4的结果为0在C或者类C语言中.&的表达为&
.| 位或 整数 整数 位或例如3 .| 1 的结果33 .| 4的结果为7在C或者类C语言中.|的表达为|
.! 位非(取反) 整数 整数 位非,.! 1的结果为-2.! 0的结果为-1在C或者类C语言中位非运算符为~
.^ 位异或 整数 整数 位异或按位异或相同位为0不同的位为11 .^ 1为01 .^ 5 的值为4在C或者类C语言中.^的表达为^
SHL 位左移 整数 整数 位向左移动最高移动的N位舍弃
ROL 左循环移位 整数 整数 位向左移动最高移动的N位卷滚到最低位
SHR 位右移 整数 整数 位向右移动最低移动的N位舍弃
ROR 右循环移位 整数 整数 位向右移动最低移动的N位卷滚到最高位

[]运算符

[]是对特殊的运算符,和()类似,他们总是成对出现的,有[的时候,必定有匹配的]作为结尾。

我们在前面的章节里看到过 MOV EAX,[EBP+4]这样的指令,这里的[]是取地址 EBP+4 位置存贮的双字,而前面我们也提到计算机的内存也像是一个数组一样,地址像是数组的下标。

在 TSL 语言中,[]可以有三个用途,一个指示数组的下标项。

例如:

A:=array(1,2,3);

B:=A[0];

那么 B 的结果是 1;

[]中的内容就是数组的下标。

如果我们替换掉一行语句

A:=array(1,2,3);

A[0]:=2;

那么数组 A 的内容就变成了 array(2,2,3)。

[]的另外一个作用就是指示字符串中指定下标的字符。

字符串在概念上与字符数组很类似,例如:

"ABCD"这个字符串,我们可以类似地认为是一个长度为 4 的字符数组,我们也借助数组中的下标与项的概念,认为字符串是数字下标数组,而项的类型则为字符。在 TSL 中,字符串的下标是从 1 开始的。在其他语言中Pascal 语言的字符串的下标也是以 1 开始,但是 C 以及类 C 的语言中,字符串的下标从 0 开始。

在 TSL 中,并不存在字符这种类型,字符类型被以长度为 1 的字符串所替代,要么以其 ASCII 码值来替代。

例如:

A:="ABCD";

B:=A[1];

那么 B 的结果是"A"

如果要给字符串的某个项来赋值:

A:="ABCD";

A[1]:="B";

那么 A 的值变成了"BBCD"

我们也可以用下列方法给字符串的某项赋值:

A:="ABCD";

A[1]:=0x30;

结果 A 的值变成了"0BCD",因为 16 进制的 30 就是字符 0 的 ASCII 值。

此外,[]算符还可以用于在类 SQL 语法中取字段结果,这个内容在今后的数据查询与 SQL 语言中将会进行具体描述。

[]算符在数组和字符串下标运算中还可以支持子矩阵和子串运算,在这里我们也不做说明,留在后边章节做详细说明。

表达式运算符

说到表达式运算符,就需要提到什么是表达式,常量、变量、函数等都是表达式,用算符连接起来的常量、变量、函数等仍然是表达式。

什么是表达式运算符呢?

例如

a:=1;

b:=a+1;

那么我们知道 b 的结果会是 2我们知道 a+1 就是一个表达式,下边的代码:

A:=1;

B:=@A+1;

C:=eval(B);

这里边出现了一个表达式求值的函数,叫 eval而 B 是表达式类型,利用表达式求值函数对 B 这个表达式求值得到的结果就是 2也就是 C 的结果是 2。

执行下边的代码可以得到同样的结果:

A:=1;

B:=&”A+1”;

C:=eval(B);

@符是一个表达式前导符,用于指定后边的内容是一个表达式,而&符号则是把一个字符串的内容编译成为一个表达式使用。

运算符 运算 运算对象 结果类型
@ 前导符,直接声明为表达式,一元运算符 语句 表达式型
& 转换字符串为表达式,一元运算符 字符串类型 表达式型

表达式运算用于那些需要动态执行而传值无法实现的特殊情况。

对象访问符

该算符在后续章节中会使用到

运算符 运算 运算对象 结果类型
. 对象运算符号,访问对象的属性和方法 对象类型 任意

矩阵运算符

矩阵运算符号主要是用于矩阵数组TSL 语言中的基础算符 +,-,*,/,,%,mod,div,^,~,.=,.>,.<,.<>,.>=,.<=,.!,.&,.|,.^,.||,.&&,.!!,like,++,--都支持矩阵的计算用。

关于矩阵的计算我们将在矩阵计算专门章节里讲述。

运算符 运算
:* 矩阵乘
:/ 矩阵除
:\ 矩阵左除
:^ 矩阵幂
! 矩阵逆和伪逆
| 矩阵并右方
:| 矩阵并右方(空列不错位,支持非完全矩阵)
Union 矩阵并下方
:: 矩阵循环
::= 矩阵循环赋值
-> 生成序列数组

集合运算符

和矩阵计算不同,集合运算的结果会去重复记录,因此集合运算的 Union2 与矩阵计算的 Union 的结果是不同的。同样,在其他集合操作中,结果集均具有去重操作。

有关集合运算的内容在集合运算章节中会独立讲述。

运算符 运算
IN 判断元素是否存在或者判定是否是子集
Union2 集合并集
Intersect 集合交集
Outersect 对称差集
Minus 集合补集
SQLIN行判断,判别是否是元素

过滤集运算符

过滤集运算是过滤掉二维结果集中某列的内容存在或者不存在于某个一维结果集中的运算,返回的结果可以是过滤后的二维结果集,也可以是二维结果集的下标列表,事实上,结果集过滤完全可以利用 TS-SQL 的 Select 来实现,但是相对于 Select 而言,结果集过滤的效率会更高效也更容易理解。

过滤集运算也可以对二维结果集的整行内容进行过滤,这样的操作非常类似于集合运算,过滤在过滤集内的类似于交集,而反之类似于减集。但是过滤运算和集合运算的最大差异是:集合运算的结果内容不允许有重复项,而过滤运算的结果允许有重复项,也就是说于被过滤集中的重复项目是不会被自动去重的。

运算符 运算
FilterIn 过滤在其中
FilterNotIn 过滤不在其中

运算赋值运算符

什么是运算赋值运算符呢?我们先来看一条语句:

A:=A+B;

这条语句的意思读者已经很清楚了,就是将 A+B 的值赋予 A。对于这类运算我们可以用另外一条语句替代

A+=B;

这就叫运算赋值运算符,基本上就是用算符=来替代掉原来的 A:=A 算符 B 的功能。

这种算符在 C 以及类 C 语言中被广泛支持,但是或许有的读者会有一个疑问,除了获得代码编写上的简便以外,似乎没有其他用处。其实不然,在很多情况下,采用运算赋值运算符还可以获得效率上的提高。

我们拿字符串的加法来做一个例子:

A:="";

For i:=0 to 9999 do

A:=A+"A";

A:="";

For i:=0 to 9999 do

A+="A";

这两种写法在计算机的执行上是完全不同的,+=并不仅仅只是写法的简便那么简单。

在执行 A:=A+"A"的时候,计算机先取出 A 的值,然后执行+的操作,也就是需要先分配一个足够的内存空间存贮 A+"A"的结果,然后再将 A 原来存贮的字符串进行释放,释放以后再把计算的字符串结果复制到 A。

而执行 A+="A"的时候,计算机仅仅只需要先把 A 的字符串的存贮空间扩大一个字节,然后再把"A"复制进末尾。

简单说起来,就是+=可以利用原有变量的存贮空间,而:=则不行,这样就可以基本保障几乎所有类似的运算采用运算赋值运算符会更快速。这个效率尤其在之后的矩阵计算中会体现得更加明显(由于矩阵的每一次计算的结果集相比标量而言都非常庞大,导致:=会不断地生成新结果集和删除旧结果集,使得效率很低)

运算符 算符名称 备注
+= 加等于
-= 减等于
*= 乘等于
/= 除等于
\= 左除等于
^= 幂等于
~= 对数等于
%= 模等于
Div= 整除等于 很特殊的算符,字母和符号并用的算符
.|= 位或等于
.&= 位与等于
.||= 逻辑或等于
.&&= 逻辑与等于
.^= 位异或等于
|= 矩阵右并等于
:|= 矩阵右并等于 和|=的差异见后续章节中|和:|的差异
&= 矩阵下并等于 很特殊的算符,因为&是表达式,所以利用&=来实现union等于的功能
:*= 矩阵乘等于
:/= 矩阵除等于
:\= 矩阵左除等于
:^= 矩阵幂等于
Union2= 集合并等于 很特殊的算符,字母和符号并用的算符
Intersect= 集合交集等于 很特殊的算符,字母和符号并用的算符
Outersect= 集合对称差集= 很特殊的算符,字母和符号并用的算符
Minus= 集合补集等于 很特殊的算符,字母和符号并用的算符

With 设值运算符

运算符 运算 运算对象 结果类型
=> 设置With的系统参数 任意类型 无结果

算符优先级

什么是优先级呢?

例如 1+23是先计算 23再计算 1+,这就表示的优先级高于+1+23 等同于 1+(2*3)。

2>2+1是先计算 2+1再计算 2>,结果 2>3 为假,表示+的运算优先级高于>2>2+1 等同于 2>(2+1)。

当优先级一样的时候则从左至右计算。

以下会出现一些在上边未使用的算符,这些算符将会在矩阵计算等章节中详细解释。运算符的优先级有两个特例,就是+-号的优先级分为两种,一个是一元运算符的正负号的优先级,一个是做加减法计算的优先级。

运算符 优先级
(),[],.,?. 0(最高)
&,Not,++,--,`,!,.!,.!! 1
$ 2
^,~,:^ 3
*,/,\,%,Mod,Div, :*,:/,:\,SHL,SHR,ROL,ROR 4
+,-,|,:|,union,union2,Intersect,Outersect,Minus,.|,.||,.&,.&&, ->,.^,正号+,负号- 5
IS,IN,Like,=,<,>,>=,<=,<>,.=,.<>,.>=,.<=,.<,.>, :>, :<,:<>,:==,:>=,:<=,::>,::<,::<>,::==,::>=,::<= 6
And,Or 7
: 8
:=,+=,-=,*=,/=,\=,^=,~=,%=,div=,|=,:|=,union2=,Intersect=,Outersect=, Minus=,&=,:*=,:/=,:\=,:^=,::=,::,.|=,.&=,.||=,.&&=,.^= 9
@ 10(最低)

值得注意的是,虽然 TSL 语言的算符和 Pascal 语言在很多方面相似,但是算符的优先级却不尽相同,有一个很典型的例子:

在 Pascal 语法中A>1 and B<2 这样的表达式是不成立的,这是因为在 Pascal 中 And 可作为位与算符,而且优先级比>要高,那么 A>1 and B<2 等同于 A>(1 and B)<2这样的表达式显然是不对的。

而在 TSL 语法中And 为逻辑运算符,而且优先级低于>和<,这样 A>1 and B<2 就等同于(A>1) and (B<2)。在 C 或者类 C 语言中,由于逻辑运算符与位运算符是独立的,所以与 TSL 类似,在 C 语言中 A>1 && B<2 也是成立的。

正是由于运算的优先级存在很多的语言差异,而且算符优先级即使对于一个有着丰富经验的程序员而言,也是经常容易混淆的。正因为如此,笔者建议读者在不确认甚至于无论是否确认,也是用圆括弧来把优先级标出来。例如 A>1 and B<2 我们就应该写成(A>1) and (B<2),这样不仅仅阅读起来一目了然,形成这个良好的习惯对于转换于多种计算机语言开发的人也可以起到很大的帮助。

?.模式

即是对对象引用与数组访问的一个容错处理。

该功能也可以通过系统参数 CalcCTRLWord 值为 0x100 和 0x200 进行指定,与?.模式区别为,系统参数设置为全局有效,而?.模式只是在具体使用时有效。

FAQpn_CalcCTRLWord

具体为:

1、a?.b?.c 对象引用中,允许对 nil 调用返回 nil即当对象存在时则等同于 a.b.c否则返回 nil方便缺省时未创建对象在引用时可能引发报错的问题

2、t?.[]数组访问中,支持对 nil 类型操作返回 nil即当 t 为 nil 时 t?.[1]返回 nil当 t 为数组时t?.[1]等同于 t[1]

注:仅支持数组访问时,不支持数组写入时如此引用。

算符优先级:与[].等算符同级

示例 01

如对一个 nil 类型进行引用时返回 nil而非报错

 objA:=new tstringlist();

 objB:=nil;

 return array(objA?.Count,objB?.Count);

返回结果为 array(0,nil)

示例 02

如对一个 nil 类型进行指定下标访问时返回 nil而非报错

 A:=array((1,2),(2,3));

 B:=nil;

 return array(A?.[1][1],B?.[1],A[2]?.[1]);

返回结果为array(3,nil,nil)

其中B?.[1]因 B 为 nil 所以返回 nil

A[2]?.[1]因 A[2]不存在所以返回 nil

示例 03

连用模式如:

Type AAA=class

  tt;

  bb;

  function Create();

  begin

   tt:=array(1,2,3,4);

   bb:=new BBB();


  end;

end;

Type BBB=class

  s;

  function Create();

  begin

   s:="BBB";

  end;

end;

//调用

obj:=new AAA();


 echo obj?.tt?.[1]; //若对象obj或数组tt不存在时则返回nil

  echo obj?.bb?.s; //若对象obj或bb不存在时则返回nil


  return 1;

打印:

2

BBB

表达式

内容

  • 算术表达式
  • 布尔表达式
  • 逗号表达式
  • 链式表达式

算术表达式

算术表达式是由算术运算符连接常量、变量、函数的式子。算术表达式中各个运算次序为: ( ),函数,算符

布尔表达式

TSL 提供的布尔表达式可以含有逻辑运算和关系运算。

逗号表达式

语法:(exp1,exp2[,exp3,...,expN])

逗号表达式是将几个表达式串联起来,可以看做是一个一起提交的语句块,用英文括号封装。编译器先从左到右进行编译,即运算的顺序是从左到右的。如:

return (a:=1,b:=2,c:=3,a+b+c);

这个语句先对 a 赋值,在对 b 赋值,接下来是 c最后执行 a+b+c。类似于

a:=1;

b:=2;

c:=3;

return a+b+c;

也支持作为表达式参与计算,如:

A:=(b:=2,c:=3,b*c)*c;

这个结果是 233 结果为 18

链式表达式

TSL 语言中目前支持的关系运算符:

关系运算符大于小于不等于等于大于等于小于等于说明
标量关系算符><<>=>=<=非链式a<b<c相当于(a<b)<c
标量链式算符:>:<:<>:==:>=:<=链式a:<b:<c相当于(a<b) and (b<c)
矩阵关系算符.>.<.<>.=.>=.<=非链式a.<b.<c相当于(a.<b).<c
矩阵链式算符::>::<::<>::== ::>=::<=链式a::<b::<c相当于(a.<b) .&& (b.<c)

注:由于:=是赋值符号,因此链式算符等于符为:==;由于::=是循环赋值等于符号,因此矩阵链式等于为::==

内容
  • 标量链式算符
  • 矩阵链式算符
标量链式算符

链式比较表达式形式为 a :< b: < c 或 x :== y: > z 等, 运算过程中会将其隐式转换为逻辑与and连接的多个独立比较

执行顺序:从左到右依次计算每个比较

支持任意比较运算符:包括 :> :< :<> :== :>= :<=,且可混合使用

1 :< x :< 10 等价于 (1 < x) and (x < 10)

a :== b:< c :<> d 等价于 (a = b) and (b < c) and (c <> d)

示例:

a:=10;

b:=20;

return 0:<a:<b:<100;

返回1

矩阵链式算符

矩阵链式比较的表达式形式为 a ::< b:: < c 或 x ::== y::> z 等, 运算过程中会将其隐式转换为逻辑与(.&&)连接的多个独立比较

执行顺序:从左到右依次计算每个比较

支持任意比较运算符:包括::> ::< ::<> ::== ::>= ::<=,且可混合使用

a::<=b::<=c 等价于 (a.<=b).&&(b.<=c);

示例:

a:=array((1,2),(11,12),(22,23));

b:=12;

c:=array((0,12),(1011,2),(4,23));

return a::<=b::<=c;

返回:

数组与矩阵的链式算符的使用方法:

数组与矩阵的链式算符是基于每个数据点进行链式判别获得Array(1,2,-1)::<array(2,1,0)::<array(3,2,1)的结果为 array(1,0,1),

与其它矩阵算法一样也支持和标量进行计算例如array(1,2,-1)::<2::<array(3,2,1)的结果是 array(1,0,0)

TSL 还可以支持混用 fmarray 和 array 以及标量

字符串运算

内容

  • 字符串的子串处理的算符支持
  • 字符串计算的算符支持

字符串的子串处理的算符支持

和矩阵的子矩阵计算类似TSL 的字符串也支持子串计算。

为了避免产生概念混淆我们需要再重复一次TSL 中的字符串下标是从 1 开始的,而数组是从 0 开始的。

内容

  • 字符串取指定位置的字符 StringValue[Index]
  • 字符串取子串 StringValue[StartIndex:EndIndex]
  • 字符串替换子串 StringValue[StartIndex:EndIndex]:=SubString
  • 字符串删除子串 StringValue[StartIndex:EndIndex]:=""
  • 字符串插入子串 StringValue[Index:0]:=SubString

字符串取指定位置的字符 StringValue[Index]

字符串取单个字符的方法是 StringValue[Index]的模式,例如"ABCD"[1]的结果为"A"子串是,表明取第一个字符。

字符串取子串 StringValue[StartIndex:EndIndex]

字符串取子串则采用 StringValue[StartIndex:EndIndex]的模式,例如:

S:="AAABBB";

S1:=S[3:4];

S1 的结果为"AB",为第三个字符到第四个字符的值。

字符串替换子串 StringValue[StartIndex:EndIndex]:=SubString

用户往往有替换字符串的需求,我们也可以采用和取子串类似方法处理:

S:="AAABBB";

S[3:4]:="111";

S 的结果为"AA111BB"

含义是替换掉第三个字符到第四个字符的值为"111"。

字符串删除子串 StringValue[StartIndex:EndIndex]:=""

和字符串替换子串类似,假定我们要删除第三个到第四个字符串的值,我们可以给其赋值空字符串。

S:="AAABBB";

S[3:4]:="";

S 的结果则为"AABB";

字符串插入子串 StringValue[Index:0]:=SubString

假定我们要在指定的位置插入字符串,我们可以采用类似于替换的方法,但是结束索引号设置为 0例如

S:="AAABBB";

S[4:0]:="111";

S 的结果为"AAA111BBB"

含义是在第四个字符的位置插入"111"

字符串计算的算符支持

TSL 语言的算符支持的字符串运算主要是串相加运算取子串运算子串删除和子串替换运算。绝大多数的语言对字符串的处理采用字符串支持函数的方式除了直接的算符支持以外TSL 语言同样支持丰富的字符串函数。

字符串替换子串的功能,在 TSL 语言中有相关的函数支持,例如 AnsiReplaceStr这种确定的字符串替换的功能可以利用这些函数但是如果所需替换的字符串是模式匹配串正则表达式串那这种函数就失去作用了TSL 的正则表达式支撑函数 ParseRegExpr 可以返回匹配串所在的坐标,而这些坐标正好可以用于字符串计算算符。

内容

  • 基础算符对字符串的支持
  • $处理字符串转换与拼装

基础算符对字符串的支持

字符串支持的基础算符有:+、$、[]、+=、>,<,>=,<=,<>、Like、not Like

字符串数组支持算符:+、+=、.>,.<,.>=,.<>,.=Like、not Like 等

同时Unicode 字符串也支持以上算符。

内容
  • +字符串相加
  • 字符串比较判断
  • LIKE 字符串正则匹配判定
+字符串相加

基础算符中支持字符串运算的是+号,顾名思义,字符串相加。例如,"AAA"+"BBB"的结果是"AAABBB"。

字符串比较判断

,<,>=,<=,<>,.>,.<,.>=,.<>,.=等对于字符串的比较大小的判断是使用 ASCII 码序列从第一个字符开始逐个进行大小比较。

LIKE 字符串正则匹配判定

字符串模式匹配 LIKE

字符串"2009-1-1" like "\d+-\d+-\d+"为真

在 TSL 中Like 符和 SQL 的 Like 的含义不同TSL 的 Like 的右元素为正则表达式,左边运算元素为匹配串,结果是左边串是否匹配右边的正则表达式

在匹配字符串时,默认情况下不区分大小写,若需要区分大小写,可通过 FAQSetLikeFlag 进行设置后再判断。

如默认情况下'a' like 'A' 结果为真,而设置 LikeFlag 为 0 后,结果为否,如:

SetLikeFlag(0); //区分大小写

return 'a' like 'A';//结果为0

参见LIKE

$处理字符串转换与拼装

对于多个简单类型,如果要拼接为字符串,可以采用$算符。

例如

  A:=1;

  B:=2.34;

  C:="AAA";

  D:="A="$A$"B="$B$"C="$C;

D 的结果为 A=1B=2.34C=AAA

注:当拼接的对象是实数时,表现与 floattostr 保持一致,保留最高精度。

其中,当数值为纯小数时,表达方式可能为科学记数法

如:

return "_"$(0.0000005)$",";

返回字符串"_5E-7,"

而:

return "_"$(1.0000005)$",";

返回字符串"_1.0000005,"

块环境设置语句

内容

  • 块环境设置语句简介
  • With *,SysParamArray Do与 With **,SysParamArray Do
  • With => Do

块环境设置语句简介

在 TSL 中,有一些系统环境变量是非常重要的,例如当前的股票、当前日期时间。对于这些系统环境变量,系统提供了系统函数对其进行操作。

WITH DO 则提供了一个方便的功能。

With *,SysParamArray Do与 With **,SysParamArray Do

假定我临时需要修改系统参数,但需要执行一大堆的代码,这个时候 SpecAll 就不方便了,我们引入 With *,SysParamArray Do 的模式来解决这个问题。

假定有 a:=array(pn_stock():"SZ000002",pn_cycle():cy_1m(),pn_date():Today()+10/24);

with *,a do

begin

c:=close();

//

……….

……….

//

end;

这样,我们可以用指定的系统参数环境执行一大段的代码,运算结束后恢复特殊的系统环境变量。

而在某些极为特别的情况下,我们希望在执行的过程中不修改任何系统参数,无论是否是特殊的系统环境变量,我们可以使用 with **,a do 的模式,其含义是切换环境为 a这样原有的环境失效运算结束后恢复原有的所有的环境。

With => Do

其语法如下:

WITH [S=>StockID,T=>Time] DO Statements;

例如:

With S=>'SZ000001',T=>IntToDate(20020601) Do

Begin

C:=Close();

RC:=Ref(Close(),1);

Diff:=C-RC;

End;

其工作原理如下:

先根据 S 和 T 设置当前的股票和日期,然后执行 DO 后的语句,执行完后恢复股票和日期的环境。

参见:系统参数设置和获取函数