admin管理员组

文章数量:1547982

欢迎访问 Lu程序设计

Lu脚本教程

目  录  [页首]

1 概述
2 安装OpenLu
3 Lu基础知识
3.1 第一个程序
3.2 简单的例子
3.3 标识符

3.4 表达式及函数简介
3.5 源代码格式简介
3.6 o函数
3.7 赋值语句
3.8 常用算术运算符
3.9 逗号(冒号、分号)运算符和括号运算符
4 Lu常量
5 Lu变量
5.1 自变量
5.2 动态变量
5.3 静态变量
5.4 模块变量
5.5 全局变量
6 Lu数据类型
6.1 整数
6.2 实数
6.3 复数
6.4 三维向量
6.5 混合运算隐式转换规则
6.6 逻辑值
6.7 nil
6.8 函数(表达式)句柄
6.9 静态字符串
6.10 静态数组
6.11 动态数据类型
6.12 数组操作函数
6.13 由编译符#生成的整数

7 Lu运算符及优先级
8 Lu流程控制
9 Lu函数
10 Lu错误处理
11 Lu递归调用
12 Lu协程
13 Lu模块化编译及源程序
13.1 源程序的一般格式
13.2 程序的执行
13.3 源程序中的模块
13.4 编译指令的位置和次序
13.5 OpenLu中的模块管理
14 Lu命名空间
14.1 模块命名空间
14.2 二级函数命名空间
14.3 常量命名空间
14.4 访问命名空间
15 Lu动态内存管理
15.1 动态对象与指针
15.2 对象成员运算符(函数参数运算符)及对象赋值运算符

15.3 对象赋值及获取
15.4 动态内存管理
15.5 表达式的初始化及销毁
16 Lu标识符解释规则
17 Lu运算符及函数重载
18 Lu高级数据类型
18.1 luu表
18.2 字符串String
18.3 字典dict
18.4 结构struct
18.5 类class
19 while in循环
20 数学库中的代码矢量化及其他
21 在脚本中编译脚本
22 Lu关键字
23 OpenLu及Lu扩展库

正文

1 概述  [返回页首]

    Lu是一个可对字符串表达式进行动态编译和运行的动态链接库(dll),是一种易于扩展的轻量级嵌入式脚本,支持协程,提供自动内存管理,也可以手动管理内存,可准确定位运行错误,是一种强类型的脚本。Lu可用于连接各种语言编写的模块。Lu的优势在于简单易用和可扩展性强。

    Lu语法由核心库(Lu64.dll)、扩展库及主程序提供。本教程介绍开放式计算程序OpenLu中的语法。OpenLu由核心库Lu64.dll和模块化编译运行库MLu64.dll提供支持。MLu是程序和Lu核心库之间的一个接口库,MLu会自动加载Lu核心库和动态加载多个Lu扩展库,简化了Lu系统的使用;MLu可对Lu源程序进行模块化编译,能够编译运行具有固定格式的源程序(字符串表达式),源程序中可以使用C++风格的注释。

    请下载试用OpenLu以演示本文的例子。OpenLu是免安装的绿色软件。

    除核心库(Lu64.dll)及个别扩展库外,OpenLu、Mlu及大多数扩展库都是开源的,你可以下载OpenLu、MLu及扩展库源代码并加以修改,加入到自己的工程中。

2 安装OpenLu  [返回页首]

    OpenLu是64位Windows平台上免安装的绿色软件,将压缩包解压到合适位置即可。

    OpenLu启动时界面上有2个窗口,上面是代码窗口,下面是运算结果输出窗口。

    OpenLu工作模式有三种,可通过命令菜单进行切换:

    (1)普通编译模式:在代码窗口写好代码后,通过菜单、工具栏或快捷键F8进行编译计算。

    (2)即时编译模式:在代码窗口写代码时,即时给出代码中的错误。

    (3)即时编译计算模式:在代码窗口写代码时,即时给出代码中的错误,若没有错误,将进行计算给出结果。

    为了更好地使用OpenLu时,建议进行以下操作:

    (1)用管理员身份运行OpenLu。用OpenLu打开文件夹“Ini”中的文件“OpenLu.ini”(通常会提示该文件已经打开),或者其他自定义的工作区文件。

        a、执行菜单命令:设置 -> 设置当前文件为工作区。

        b、执行菜单命令:设置 -> 设置当前文件为缺省工作区。

    (2)给OpenLu创建一个快捷方式,然后把该快捷方式放在桌面上或“开始”菜单中。

3 Lu基础知识

3.1 第一个程序  [返回页首]

    对于大多数程序语言,第一个入门编程代码便是"Hello World!",以下是Lu脚本中输出"Hello World!"的代码。

      o("Hello, World!");

    o是一个函数(取对象object或者输出out的首字母),用于输出对象的信息,"Hello World!"是一个字符串对象,字符串要放在两个双引号之间。输出结果如下:

      Hello, World!13

    输出结果的末尾13是o函数的返回值,o函数返回输出的信息串的长度。如果不希望出现o函数的返回值, 需要在o函数后使用两个连续的分号,如下列:

      o("你好,世界!");;

    输出结果如下:

      你好,世界!

3.2 简单的例子  [返回页首]

      F(x,y)=x+y;       //函数定义,恐怕是所有语言中最简单的了
      2+F[2,3]+5;       //简单的计算式

    在这个例子中,分号表示一个表达式的结束,两个反斜杠//后的内容表示注释,MLu中采用这种表示和注释方法。
    在第一个表达式中定义了一个函数 F ,小括号( )内的x,y为函数的自变量,等号后为函数体,即可执行的部分,该函数计算并返回两个参数的和。在第二个表达式中调用了上面定义的函数 F 。
    需要注意,在一般的Lu程序中,只计算无自变量的表达式。因此,对第一个表达式只编译,不计算。

3.3 标识符  [返回页首]

    在Lu中,一个标识符可以用来表示一个表达式的名字,或者是一个函数的名字,或者是一个变量,或者是一个符号常量。标识符可由一个或多个字符组成,可以任意一个英文字母、中文字符或者下划线开头,后面可以接英文字母、中文字符、数字或者下划线(下划线的使用可以增加标识符的可读性,如first_name)。注意:英文字母的大写和小写是不同的,例如:count和COUNT是两个不同的名字。下面给出一些合法的标识符:

      first    last    Addr1    top  _of_file    name23    _temp    a23e3    MyVar    您好    数1    My数y数

3.4 表达式及函数简介  [返回页首]

    表达式是Lu的编译单元,如果没有语法错误,编译后将生成可解释执行的中间代码。

    Lu表达式可以很简单,例如只有一个数字;Lu表达式也可以很复杂,例如是一段复杂的程序。以下是一些简单的表达式例子:

      2;
      2+3;
      2.0+sin(3.0)-ln[6.0];

    Lu表达式有无名表达式和有名表达式两种,有名表达式即函数,如下例:

      A( )=2;
      B( )=2+3;
      C( )=2.0+sin(3.0)-ln[6.0];

    给表达式起一个名字,使之成为函数,这样做的好处是:以后可以用函数调用的方式通过该名字调用该表达式。表达式的名字是一个标识符,必须位于表达式的开头。

    表达式(函数)定义的一般形式是:

      Name(a,b) = {a=2, b=10 : b=b+a; a+b}

    其中 Name 为表达式的名字,不可与其它已经定义的表达式名字相同,但可以缺省;如果定义了表达式名字,表达式名字后必须跟一对小括号(只能用小括号),用来定义自变量,自变量个数可以为零,也可以有任意多个,有多个自变量时,自变量间以逗号分隔;如果用小括号定义了自变量,小括号后必须跟一个等号,该等号标志表达式可执行部分的开始,等号及等号后的可执行部分均不可缺省。

    表达式的可执行部分由多个语句组成,多个语句之间用三个等价的逗号、冒号或分号分隔以增强可读性,表达式总是返回最后一个语句的值;可执行部分中可使用三对等价的括号( )、[ ]和{ }以增强可读性;另外最外层的花括号不是必须的,但有了花括号后,表达式更易读。

    下面是一个函数定义及使用的例子。

      相加(加数1,加数2)=加数1+加数2;    //函数定义
      2+相加[2,3]+5;                   //函数调用

    以下是一些合法的表达式例子:

      2;                //常量(无参)表达式
      ( )=2;          //常量(无参)表达式
      A( )=2;       //常量(无参)表达式(函数),在其它表达式中可以调用该函数
      B(x)=2;       //有一个自变量的表达式(函数)
      C(x,y)=x+y;   //有二个自变量的表达式(函数)

    以下代码的可读性强:

      new[reals, 3, 2, data : 1.1, 2.2; 3.3, 4.4; 5.5, 6.6];    //new函数生成3×2二维数组(矩阵)并赋初值, 使用三个等价的逗号、冒号及分号分隔数据

      f(x)=x^3*ln{abs[(x^2-1)*(x^2-2)]};   //定义函数f,使用三对等价的括号

3.5 源代码格式简介  [返回页首]

    MLu源代码文件由若干函数(或表达式)组成,函数(或表达式)由分号分隔,源代码中可使用C++风格的注释。如下例:

//每行中两个反斜杠后的内容为注释

/*

这是多行注释。

这是多行注释。

*/

f(x,y)=x+y;  //该函数只编译,不计算

2.5+sin[1.2-cos(0.8)]+f[2,3];  //该表达式计算时调用了前面定义的函数f

sin[2.3-5i];  //i表示一个虚数 。2.3-5i等价于{2.3$5},二者都是复数的表示形式

new[reals,3,2, data : 1.1,2.2; 3.3,4.4; 5.5,6.6].math::outa[];; //new函数生成二维数组(矩阵)并赋初值,然后用数学库math的函数outa输出了该矩阵,两个连续的冒号::是命名空间成员访问符

    如何区分表达式中的分号和分隔表达式的分号呢?很简单,通常在括号中的分号是表达式中的分号,否则是分隔表达式的分号。

    注意:MLu只顺序执行不带参数的表达式并输出计算结果。如果该表达式以两个连续的分号“;;”结尾,则该表达式只执行,不输出计算结果。

    以上代码执行结果如下:

      7.982313067962473
      {55.33874541030041$49.43981990995805}

      1.1   2.2
      3.3   4.4
      5.5   6.6

    Lu代码格式自由,但除了即时运行无需保存的代码外,代码书写要便于阅读和维护,需要在代码中合理地使用注释、缩进、 换行、添加空格等。

3.6 o函数  [返回页首]

    格式:o{p1, p2, p3, ... ...}

    获得对象p1,p2,p3,... ...的信息。函数返回输出的信息串的长度。

    可直接通过o函数输出的内置对象有:整数、实数、复数、三维向量、nil、表达式句柄、逻辑值、字符串。

    o函数是被重载的,故该函数执行时,对象p1,p2,p3,... ...的重载o函数将被依次调用。

    不是所有的对象都支持o函数,具体需要查看该对象的说明。

    例子:

    o["x=", 2, ", y=", 3, ", x+y=", 2+3, "\r\n"];

    结果:

    x=2, y=3, x+y=5
    17

    注意:为什么信息串的长度是17呢?因为还包含最后的两个字符"\r\n"。“\r”是转义字符,表示回车,只有一个字符;“\n”也是转义字符,表示换行,也只有一个字符。

3.7 赋值语句  [返回页首]

    Lu脚本中,变量可赋予不同的值,存储不同的数据类型,变量的赋值用等号“=”进行,等号左边是一个变量名,等号右边是存储在变量中的值。 例如:

      //该表达式中没有自变量,a和b为动态变量(冒号前为自变量,冒号后为动态变量),动态变量只在表达式执行时起作用,一旦表达式执行完毕,动态变量也随之失效

      f( : a, b) = {a=1, b=2, a+b}; //a和b为动态变量,分别赋值1和2,然后计算a+b

    结果:

      3

    可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量(这种变量本身类型不固定的语言称之为动态语言),例如:

      f( : a ) =  //a是动态变量
      {
          a = 1,
 //a是整数

          o["a=", a, "\r\n"],

          a = "abc", //a是字符串

          o["a=", a, "\r\n"]
      };;

    结果:

      a=1
      a=abc

    注意不要把赋值语句的等号等同于数学的等号,例如:

      f( : a ) =  //a是动态变量
      {
          a = 1,

          a = a + 2,

          o["a=", a, "\r\n"]
      };;

    结果:

      a=3

    在数学上理解 a = a + 2 是不成立的,在程序中,赋值语句先计算右侧的表达式 a + 2,得到结果 3 ,然后再赋值给 a 。

    给一个变量 a 赋值,可以理解成将 a 指向某数据,把一个变量 a 赋值给另一个变量 b ,实际上是把变量 b 指向变量 a 所指向的数据,例如:

      f( : a, b ) =
      {
          a = "abc",
 //a指向数据"abc"

          b = a,    //b指向a所指向的数据,也是"abc"

          a = "xyz", //a指向数据"xyz"

          o["b=", b, "\r\n"]
      };;

    结果:

      b=abc

3.8 常用算术运算符  [返回页首]

    常用的算术运算符有:+(加或正)、-(减或负)、*(乘)、/(除)、%(求模)、^(乘方)、++(自增)、--(自减)等等。

    运算符*、/、%和 ^ 是双目运算符。注意数字与变量相乘时,乘号不可省略;在进行乘方运算时,底数应为非负数。

    运算符+、-作加、减运算时,是二元运算符,当作正、负运算时,是单目运算符。

    运算符++、--是单目运算符,仅能对变量使用。

    ++使变量的值加1,如果++在变量之前,那么运算符在程序语句访问该值之前执行加法运算,这时的++运算符称为“前置自增运算符”;如果把该运算符放在变量之后,那么运算符在程序语句访问该值之后执行加法运算,这时的++运算符被称为“后置自增运算符”。

    --使变量的值减1,如果--在变量之前,那么运算符在程序语句访问该值之前执行减法运算,这时的--运算符称为“前置自减运算符”;如果把该运算符放在变量之后,那么运算符在程序语句访问该值之后执行减法运算,这时的--运算符被称为“后置自减运算符”。

    例如:

      (:x)= x=2, ++x;    //返回值为3

      (:x)= x=2, ++x, x;  //返回值为3

      (:x)= x=2, x++;    //返回值为2

      (:x)= x=2, x++, x;  //返回值为3

      (:x)= x=2, --x;    //返回值为1

      (:x)= x=2, --x, x;  //返回值为1

      (:x)= x=2, x--;    //返回值为2

      (:x)= x=2, x--, x;  //返回值为1

    单目运算符的优先级比双目运算符的优先级高,后置单目运算符的优先级比前置单目运算符的优先级高。对于同一优先级的运算,按从左到右的优先顺序进行。

    注意:单目运算符-(负) 与双目运算符^(乘方)需用括号区分计算的先后顺序。例如:

      (-2)^2;       //返回值为4

      -(2^2);       //返回值为-4

      -2^2;         //编译出错

      -(2+3)^2;     //编译出错

      -sin(2.+3)^2; //编译出错

    算术运算符的优先级如下表所示。在表中,同一行中运算符优先级相同,不同行中运算符的优先级从上往下依次降低。

常用算术运算符及优先级

运 算 符

说  明

++、-- 后置单目运算符,自增减运算符
+、-、++、-- 前置单目运算符,“++、--”为自增减运算符
^ 乘方
*、/、% 乘、除、求模
+、- 加、减


3.9 逗号(冒号、分号)运算符和括号运算符  [返回页首]

    表达式中如果有多个语句,可以用逗号、冒号或分号进行分隔,Lu将按从左到右的顺序计算各个语句,并返回最后一个语句的值。也可以将多个用逗号、冒号 或分号分隔的语句放在一个括号内,Lu也将按从左到右的顺序计算各个语句,并返回最后一个语句的值。

    逗号(冒号、分号)运算符和括号运算符使得Lu脚本中可将多条语句放在一行内,仍有很强的可读性。

    例如:

      (:x)= x=2, x=5, x;                   //返回值为5

      (:x)={x=2, x=5, x};                  //返回值为5

      (:x, y)={x=2, y=5, x=[x=x+1, y=y+1, x+y] : x};//返回值为9

      (:x, y)={x=2, y=5, [x=x+1, y=y+1 : x+y]};   //返回值为9

    虽然逗号、冒号或分号运算符是通用的,但逗号的用法很常见。冒号和分号运算符仅用在一些特殊场合,例如分隔一些不同类别的变量。

    注意:一般表达式之间用分号进行分隔,故分号运算符必须放到括号内才有效。如下例:

      (:x, y)={x=2, y=5; x=[x=x+1, y=y+1; x+y] : x}; //返回值为9

      (:x, y)={x=2, y=5, [x=x+1; y=y+1 : x+y]};   //返回值为9

4 Lu常量  [返回页首]

    常量是指那些不需要程序计算的固定值。有数值常量和符号常量两种。如100就是一个数值常量。在Lu中,也可以用标识符表示一个符号常量。用函数const可以定义一个永久性符号常量或暂时性符号常量:

    设置常量:const("ConstStr",ConstValue) 或 const("ConstStr",ConstValue,bMode)

    ConstStr:常量名,要符合Lu标识符的命名规定,否则编译器找不到该常量。可使用常量命名空间的方式命名,例如:"ConstName::MyConst",在这里先忽略这种命名方式。
    ConstValue:常量的值,只能是Lu基本数据类型所能保存的数据,例如整数、实数、复数、三维向量、长精度实数和逻辑值等;如果是字符串等动态对象,只保存该对象的指针,如果该对象被销毁,该常量的值无意义。当bMode为逻辑假时,该值被忽略。
    bMode:逻辑值,指出工作方式。若缺省该参数(函数const只有两个参数),创建一个永久性常量,无法删除一个永久性常量,除非Lu重新初始化。若bMode为true,创建一个暂时性常量;若bMode为false,删除一个暂时性常量。暂时性常量保持常量的基本意义,在编译表达式时不可改变其值,但可被const函数删除。

    该函数返回值的意义如下:

      0:设置成功。
      1:已存在该常量。
      2:内存分配失败。
      3:不能用空字符串作为常量名。
      4:参数不符合要求。
      5:需用字符串指出常量名。
      6:不能删除该常量。

    只有const函数被执行后,定义的常量才会起作用。如下例:

      !!!const("pi",3.1415926);  //定义永久性常量,不可删除 ;行首的!!!表示该表达式编译后立即执行(且以后不再执行),否则要等到全部源代码编译完后执行
      !!!const("aa",222,true);   //定义暂时性常量,可以删除

      pi;           //pi=3.1415926
      aa;           //aa=222
      const("pi",0,false);  //返回值为6。试图删除永久性常量,但失败了
      const("aa",0,false);  //返回值为0。删除暂时性常量

5 Lu变量  [返回页首]

    变量是指在程序运行时,其值可以改变的量。Lu有五种变量,即:自变量、动态变量、静态变量、模块变量和全局变量。

    自变量、动态变量和静态变量只能被定义该变量的表达式所访问;

    模块变量可被同一模块的所有表达式所访问,其他模块的表达式无法访问;

    全局变量可被所有的表达式所访问。

    自变量用于向表达式传递参数,因此自变量也称为形式参数。

    动态变量只在表达式执行时起作用,一旦表达式执行完毕,动态变量也随之失效。

    静态变量存在于表达式的整个生命周期,每次表达式执行完毕,静态变量的值被保留。

    Lu在编译表达式时,自变量不进行初始化;动态变量初始化为nil;静态变量初始化为0;模块变量和全局变量在第一次生成时初始化为nil,以后使用不再进行初始化。

    如果表达式中的变量名与一个常量名相同,则常量名被忽略。

    在Lu表达式中,通常情况下,变量要先定义后使用,变量在表达式的开头进行定义,格式如下:

      F(a, b
        
: x, y, static, u
        : s, t, common, v ) =
      {
        x=1,y=2,
        a+b+x+y+
static+u+s+t+common+v
      }

    F是表达式的名字,a和b是自变量,x和y是动态变量,static和u是静态变量,s和t是模块变量,common和v是全局变量。

    自变量、动态变量和静态变量以及模块变量和全局变量之间用冒号分隔,即第一个冒号前为自变量,两个冒号之间为动态变量和静态变量,第二个冒号后为模块变量和全局变量。

    两个冒号之间用关键字static分隔动态变量和静态变量,static之前为动态变量,static及以后变量均为静态变量,关键字static只能用在两个冒号之间。

    第二个冒号后用关键字common分隔模块变量和全局变量,common之前为模块变量,common及以后变量均为全局变量,关键字common只能用在第二个冒号后。

    Lu中的所有变量均可缺省。

    以下都是合法的变量定义的例子:

      F()= ... ...          //没有使用任何变量,称无参表达式

      F(::)= ... ...        //没有使用任何变量,称无参表达式

      F(a, b)= ... ...      //定义了两个自变量a和b

      F(: x, y)= ... ...     //定义了两个动态变量x和y

      F(: static, u)= ... ... //定义了两个静态变量static和u

      F(: : s, t)= ... ...      //定义了两个模块变量s和t

      F(: : common, v)= ... ...//定义了两个全局变量common和v

      F(a, b : x, y)= ... ...   //定义了两个自变量a和b,两个动态变量x和y

      F(a, b : : s, t)= ... ...  //定义了两个自变量a和b,两个模块变量s和t

      F(: x, y : s, t)= ... ...  //定义了两个动态变量x和y,两个模块变量s和t

5.1 自变量  [返回页首]

    自变量是函数(表达式)参数中声明的变量,用于向表达式传递参数,称为形式参数 ,自变量只能在函数内部使用。例如:

      f(x,y)=x+y; //该函数 中x和y即自变量

      2+f[2,3];   //向函数f传递参数2和3

    结果:

      7

5.2 动态变量  [返回页首]

    动态变量 只能在函数内部使用,只在函数执行时起作用,一旦函数执行完毕,动态变量也随之失效。例如:

      f(x,y : a, b) = {a=1, b=2, x+y+a+b}; //该函数 中x和y为自变量,a和b为动态变量

      2+f[2,3];   //向函数f传递参数2和3

    结果:

      10

5.3 静态变量  [返回页首]

    静态变量存在于函数 (表达式)的整个生命周期,每次函数执行完毕,静态变量的值被保留。静态变量初始化为0。例如:

      f(x,y : static, a) =  //该函数中x和y为自变量,a为静态变量
      {
          o["a=", a, "\r\n"],  
//用o函数输出a的值,"\r\n"表示输出一个换行符
          a++,       //a的值增1,++是自增运算符
          x+y+a
      };

      f[2,3];
      f[2,3];
      f[2,3];

    结果:

      a=0
      6
      a=1
      7
      a=2
      8

5.4 模块变量  [返回页首]

    模块变量可被同一模块的所有表达式所访问,其他模块的表达式无法访问。模块变量在第一次生成时初始化为nil。

    Mlu编译时,将源程序中的函数(表达式)编译为一个或多个模块。编译时首先设置起始模块,也称主模块,以后每当遇到#MODULE#,开始编译为一个新的模块,称为子模块,而每当遇到#END#,回到主模块的编译。即#MODULE##END#之间的表达式定义为一个子模块,子模块之间不能嵌套定义。注意#MODULE##END#必须位于表达式的开头。

    在模块中,以:::开头的表达式被编译为公有表达式(全局表达式或全局函数),能被其他模块访问到,其余的表达式均被编译为私有表达式(私有函数),其他模块无法访问。另外,若表达式名称前有编译符!!!,在编译后将立即执行;若表达式名称前有编译符~~~,只编译,不执行。

    (1)模块源文件的格式

      !!!const("pi",3.1415926);  //主模块中的表达式,定义永久性常量,该表达式是在编译时执行的

      #MODULE#              //定义一个子模块

        a(x)=x+1;            //私有函数,只能被该模块的表达式所访问

        :::b(x)=a(x)+2;        //全局函数,任意模块包括本模块均可访问

      #END#                  //子模块定义结束,可缺省

      #MODULE#              //定义一个子模块

        a(x)=x-1;           //私有函数,只能被该模块的表达式所访问

        :::c(x)=a(x)+2;       //全局函数,任意模块包括本模块均可访问

      #END#                  //子模块定义结束,不可缺省

      a(0);                  //主模块中的表达式 ,编译时将提示“不可识别函数名”

      b(0);                  //主模块中的表达式

      c(0);                 //主模块中的表达式

    结果(去掉主模块中的a函数调用):

      3
      1

    (2)模块变量例子

      #MODULE#              //定义一个子模块

        !!!a( :: s) = s=1;        //私有函数, 编译时立即执行,使得模块变量s=1

        :::b(x :: s) = s+x;        //全局函数,任意模块包括本模块均可访问

      #MODULE#              //定义一个子模块

        !!!a( :: s) = s=-1;        //私有函数, 编译时立即执行,使得模块变量s=-1

        :::c(x :: s) = s+x;        //全局函数,任意模块包括本模块均可访问

      #END#                  //子模块定义结束,不可缺省

      b(1);                  //主模块中的表达式

      c(1);                 //主模块中的表达式

    结果:

      2
      0

    (3)使用未定义的模块变量

    在MLu中,可使用编译符mvar:通知编译器使用未定义的模块变量 (这并不是一个好方法,因为会由于一些相似的变量的误用而导致难以查找的错误,但这个方法使代码看起来简单,故本文某些地方也使用该编译符;在大的工程中,建议将变量定义在函数中迫使编译器对变量进行检查),使用编译符unmvar:通知编译器取消这种设置,格式如下:

      mvar:        //通知编译器使用未定义的模块变量

      s, t, u=5, v=6; //如果不存在同名的常量,就解释为模块变量

      s=1, 2+s+u;  //可以正确编译,返回值=8

      unmvar:      //通知编译器取消使用未定义的模块变量

      2+s+u;       //编译出错,变量不可识别

    根据标识符解释规则,确定变量或常量的顺行是:变量、常量、常量命名空间(在这里先忽略)。若要确保编译器将未定义的标识符解释为模块变量,则应在前面编译的表达式中,将该标识符明确地定义为模块变量并至少使用一次,如下例:

      !!!const["v",8,true]; //创建一个常量v=8

      v;            //v为常量,返回值=8

      mvar:          //通知编译器使用未定义的模块变量

      v;           //v仍为常量,返回值=8

      (::v)= v=6;   //将v定义为模块变量并至少使用一次 ,返回值=6

      v;          //v将解释为模块变量,返回值=6

      unmvar:        //通知编译器取消使用未定义的模块变量

      v;          //v将解释为常量,返回值=8

    (4)将源文件保存为磁盘模块文件来使用

    模块源文件如下:

      //模块名:myModule

      #MODULE#              //定义一个子模块

        !!!a( :: s) = s=1;        //私有函数, 编译时立即执行,使得模块变量s=1

        :::b(x :: s) = s+x;      //全局函数,任意模块包括本模块均可访问

      #END#               //子模块定义结束,不可缺省

      :::c(x) = b(x)+1;         //主模块中的全局函数,只有定义为全局函数,才能被外部使用

    将以上源文件命名为“myModule.txt”(文件命名没有特殊要求),保存到目录“OpenLu64\module”(即程序OpenLu64所在的文件夹module)中。

    在源程序的任何地方,可用指令#USE#调用另一个模块。

      #USE# module\myModule.txt;  //关键字USE必须为大写 ,后面给出模块文件名(必要时包含完整路径)。

      b(0);      //调用myModule模块中定义的全局函数

      c(0);      //调用myModule模块中定义的全局函数

    结果:

      1
      2

    也可以将模块文件加到OpenLu工作区中,使用更方便,不过需要设置一下。

    打开文件夹ini中的“OpenLu.ini”文件(OpenLu工作区设置文件),找到关键字“#MODULE”(用于设置模块),在前面添加如下代码:

      #MODULE
      {
        //定义模块“myModule”,模块文件为“Module\myModule.txt”。

        "myModule*Module\myModule.txt"

        ... ...
      }

    执行菜单“设置->重载模块文件”,使新加入的模块可用。

    如此,可用指令#USE#调用新加入的模块myModule。

      #USE# myModule;  //关键字USE必须为大写 。myModule是模块名称,myModule必须在OpenLu工作区设置文件中用关键字“#MODULE”说明

      b(0);      //调用myModule模块中定义的全局函数

      c(0);      //调用myModule模块中定义的全局函数

    结果:

      1
      2

5.5 全局变量  [返回页首]

    全局变量可被所有的函数(表达式)所访问。全局变量的使用要避免命名重名,尽量少用全局变量。

      #MODULE#       //定义一个子模块

        a( :: common, s) = s=1;;  //私有函数,全局变量s=1

      #END#       //子模块定义结束,不可缺省

      a();         //主模块中的表达式,编译时将提示“不可识别函数名”

      ( :: common, s) = s;  //主模块中的无参表达式,可以获取全局变量的值

    结果(去掉主模块中的a函数调用):

      1

6 Lu数据类型  [返回页首]

    Lu表达式中会存在许多类型的数据,如整数、实数、字符串、逻辑值、函数句柄等等,但在其内部实现上,所有的数据类型都使用同一种结构形式,即:

    typedef __int64 luIFOR;    //Lu整数类型定义
    typedef __int32 luKEY;     //Lu键值定义

struct LuData{    //Lu基本数据结构
    luIFOR x;     //存放数据。对于动态数据类型,对象指针约定保存在x中
    luIFOR y;     //存放数据
    luIFOR z;     //存放数据
    luKEY  VType; //扩展数据类型,决定重载函数
    luKEY  BType; //基本数据类型,决定数据结构
};

    任何Lu数据在赋值时,必须提供基本数据类型BType和扩展数据类型VTypeBType决定了实际的数据结构;VType指出要调用哪一个重载函数。

    根据实际需要,Lu基本数据类型也可以定义为以下形式或者其他形式(任何数据长度不超过24字节的数据都可以直接保存):

struct LuDataF{   //Lu基本数据结构
    double x;
    double y;
    double z;
    luKEY  VType; 
//扩展数据类型,决定重载函数
    luKEY  BType; //基本数据类型,决定数据结构
};

    要看懂以上内容,需要一些C/C++的基础知识。看不懂也不要紧,不影响学习以下内容。

    Lu基本数据类型共32个字节长度,类型说明占8个字节,剩余24个字节用于保存数据。整数、实数、复数、三维向量、nil、逻辑值等均可直接用基本数据表示;另外,Lu脚本在函数(表达式)中可开辟一定空间用于保存静态字符串及其衍生的静态数组类型;其他Lu数据需要动态申请后使用,称动态数据类型,动态数据类型通常用函数new或者其他专用函数生成,用完后用del或者delete函数释放,或者由Lu的垃圾回收系统回收。

    Lu基本数据类型及其静态数据类型的运行速度高于动态数据类型。

    Lu常量和变量的存储空间与Lu基本数据类型相同,故可直接保存Lu基本数据类型;其他Lu数据在常量和变量中只保存一个指向该数据的指针,该指针是否有效在Lu系统的内部是要做检验的,故在脚本中我们无需考虑,如果指针无效,脚本运行时将返回一个运行错误。

    由以上可以知道,变量赋值时,如果数据大小不超过Lu基本数据类型,就直接保存该数据(视同指向该数据),否则保存一个指针指向该数据。

6.1 整数  [返回页首]

    整数是没有小数部分的数字,整数运算永远是精确的。Lu整数是64位的(范围从-9223372036854775808~9223372036854775807,如果整数太大或者太小会发生溢出,须做检测),既可以是10进制数,也可以是16进制数,但数字中不能包含小数点,也不能用科学记数法表示数字。16进制整数以0x开头,并用A~F表示10~16。

    例子:

      12

      0x1A

      0x123D

    在整数运算中,除法运算需要特别注意:(1)除数不能为0;(2)两个整数相除仍然是一个整数。

    例如:

      10/2;  //结果为5

      10/3;  //结果为3,余数被舍弃

    小学中我们学习过四则运算法则:当一级运算(加减)和二级运算(乘除)同时出现在一个式子中时,它们的运算顺序是先乘除,后加减,如果有括号就先算括号内后算括号外,同一级运算顺序是从左到右 :

      1+2*3;  //结果为7

      1+2*(3+4);  //结果为15

    Lu表达式中的运算法则与此类似,但由于运算符多,情况更加复杂,建议用括号区分计算顺序,因为括号里面的总是要先算的。

    Lu核心库中整数运算的运算符及优先级(同一类型的运算符优先级相同,不同类型运算符的优先级从上往下依次降低)如下表(初值 a=10, b=3, c=2, d=0):

运算符类型

运算符 名称 例子 结果

说  明

后置单目运算符 ++ 后置自增 a++ 10 执行 e = a + b++ 后,e=13,b=4
-- 后置自减 a-- 10 执行 e = a + b-- 后,e=13,b=2
前置单目运算符 ! !a false

对整数求非时,返回逻辑值,且规定 !0 = true,其他情况均返回false

!d true
+ +a 10
- -a -10
++ 前置自增 ++a 11 执行 e = ++a + b 后,e=14,a=11
-- 前置自减 --a 9 执行 e = --a + b 后,e=12,a=9
!! 按位非 !!a -11 整型数据在内存中以补码表示,请查阅相关资料以理解此运算
乘方算术运算符 ^ 乘方 a^b 1000. 结果为实数
乘除算术运算符 * a*b 30

/ 左除 a/b 3 两个整数相除仍然是一个整数,余数被舍弃

整数/0将返回一个运行错误,运行错误说明:Integer division by zero,错误代码LuErr=1

a/c 5
c/a 0
% 求模 a%b 1 表示 a 除以 b 的余数
加减算术运算符 + a+b 13
- a-b 7
移位运算符 << 左移位 a<<1 20 整型数据在内存中以补码表示,请查阅相关资料以理解此运算

如何对非整型数据做移位运算呢?可先用cast函数对数据做强制类型转换就可以了,例如:

(:a)= a=1.2, a=cast(a,2), a>>1;  //cast(a,2)将数据a转换为整数类型,2表示整数

a<<5 320
a<<(-2) -9223372036854775808
>> 右移位 a>>1 5
a>>5 0
a>>(-2) 0
关系运算符 > 大于 a>b true 关系运算返回逻辑值
>= 大于等于 a>=b true
< 小于 a<b false
<= 小于等于 a<=b false
== 等于 a==b false
!= 不等于 a!=b true
按位与 && 按位与 a&&b 2 整型数据在内存中以补码表示,请查阅相关资料以理解此运算
按位异或 ~~ 按位异或 a~~b 9
按位或 || 按位或 a||b 11
$ a$b 10+3i或者{10.$3.} 并运算的结果为复数或三维向量
a$b$c {10.$3.$2.}

6.2 实数  [返回页首]

    实数也称浮点数或者小数,是含有小数点的数字,可以用科学记数法表示;浮点数运算则可能会有四舍五入的误差。Lu使用64位双精度实数(范围大致从±1.7E-308~±1.7E+308)。

    例子:

      0.0

      0.

      .0

      1.2

      -1.2E-3

    Lu核心库中实数运算的运算符及优先级(同一类型的运算符优先级相同,不同类型运算符的优先级从上往下依次降低)如下表(初值 a=10.0, b=3.1, c=2.0, d=0.0):

运算符类型

运算符 名称 例子 结果

说  明

后置单目运算符 ++ 后置自增 a++ 10. 执行 e = a + b++ 后,e=13.1,b=4.1
b++ 3.1
-- 后置自减 a-- 10. 执行 e = a + b-- 后,e=13.1,b=2.1
b-- 3.1
前置单目运算符 ! !a false 对实数求非时,返回逻辑值,且规定 !0.0 = true,其他情况均返回false
!d true
+ +a 10.
- -a -10.
++ 前置自增 ++a 11. 执行 e = ++a + b 后,e=14.1,a=11.
++b 4.1
-- 前置自减 --a 9. 执行 e = --a + b 后,e=12.1,a=9.
--b 2.1
乘方算术运算符 ^ 乘方 a^b 1258.925411794168
乘除算术运算符 * a*b 31.
/ 左除 a/b 3.225806451612903 实数/0.0将返回1.#INF,表示无穷大
a/c 5.
c/a 0.2
a/d 1.#INF
加减算术运算符 + a+b 13.1
- a-b 6.9
关系运算符 > 大于 a>b true 关系运算返回逻辑值
>= 大于等于 a>=b true
< 小于 a<b false
<= 小于等于 a<=b false
== 等于 a==b false
!= 不等于 a!=b true
$ a$b 10.0+3.1i或者{10.$3.1} 并运算的结果为复数或三维向量
a$b$c {10.$3.1$2.}

6.3 复数  [返回页首]

    Lu复数的实部和虚部都是64位双精度实数,以i结尾的数字表示复数的虚部,复数可由运算符$产生。

    例子:

      2i

      1-2i

      2$3

      -1.2E-3+2i

本文标签: 脚本教程Lu