变量的使用
变量说明
epScript 代码中的所有变量都允许非同步修改或访问。同步修改或访问它时,它就属于同步数据;非同步修改或访问它时,它就属于非同步数据。
epScript 中的值类型变量只有一种,就是 32 位无符号整数。
没有字符串值类型变量!!没有字符串值类型变量!!没有字符串值类型变量!!
-
变量声明
可以使用
var语法声明一个普通变量。var 变量名字1;static var 变量名字1 = 0;// 以上两个写法等效var 变量名字2 = 初始值; -
静态变量
事实上,所有变量在内部实现上都是静态的,不过局部变量每次都会恢复它的初始值。
没有设定初始值的变量具有静态特性;使用static关键字声明,可以显式让变量具有静态特性。function increaseCount() {static var count = 5;return count++;}function onPluginStart() {println("{}", increaseCount()); // 6println("{}", increaseCount()); // 7println("{}", increaseCount()); // 8} -
常量声明
常量的值在运行时不可变。
所有引用类型(对象)都必须用 const 修饰声明。引用类型的对象状态可以在运行时更改,但其引用的目标对象在运行时不可更改。const 数组变量名1 = EUDArray(数组大小);const 对象1 = 对象类型名(); -
变量赋值
变量的值可以在运行时更改。声明后可以使用等号(
=)对变量进行赋值。var 变量名字1; // 声明变量变量名字1 = 100; // 给变量赋值 100变量名字1 = 变量名字1 + 2; // 给变量自增 2 -
变量作用域
变量的作用域为:从声明处开始,直到离开当前层大括号(或文件)为止。内层作用域的同名变量优先级高于外层。
下面是一个错误示例:var 模块级变量 = 100;function 错误示例() {局部变量1 = 2; // 这就不行,因为下一行才声明 局部变量1var 局部变量1 = 2;{局部变量1 = 1;var 局部变量2 = 2;var 局部变量1 = 100;局部变量2 = 局部变量1; // 有两个 局部变量1 时,取作用域更小、更具体的那个,也就是 100局部变量2 = 模块级变量;}局部变量1 = 局部变量2; // 这就不行,因为 局部变量2 在里面那层大括号里声明的,它不能离开它的作用域局部变量1 = 模块级变量;} -
变量的数学运算
虽然变量只能是 32 位无符号整数,但也可以像 C 语言的无符号 32 位整数那样表示负数。
var a = 0;a -= 2;if (a > 0) { // 这个条件是成立的,无符号数的负数是大于正数的println("a == 0x{:x}", a); // a == 0xFFFFFFFE}if (a >= 0x80000000) { // 判断 a 是否为负数应该这么判断println("a(-{}) 小于 0", -a); // a(-2) 小于 0}if (a < -1 && a > -3) { // 这个是成立的println("a(0x{:x}) 小于 -1 大于 -3", a); // a(0xFFFFFFFE) 小于 -1 大于 -3}a -= 1;if (a == -3) { // 这个是成立的println("a(0x{:x}) 现在等于 -3 了", a); // a(0xFFFFFFFD) 现在等于 -3 了}var b = 0;DoActions(b.SubtractNumber(2)); // 这是无效的,Subtract 不能将数值减至 0 以下println("b == 0x{:x}", b); // b == 0x00000000 -
对象类型
对象类型是引用类型,区别于值类型。
object 对象类型名 {var 字段名1;var 字段名2;var 字段名3;function 方法名1_给字段1赋值(值) {this.字段名1 = 值;}function 获取字段1的值() {return this.字段名1;}};-
有两种方法可以创建一个对象实例
- 静态初始化:
const 对象1 = 对象类型名(); - 动态初始化:
const 对象1 = 对象类型名.alloc();你可以将它传递到任何作用域使用;用完后记得用对象类型名.free(对象1);释放它占用的内存。
- 静态初始化:
-
-
值类型、引用类型
值类型
var a = 27;var b = a;b = 3;println("{}, {}", a, b); // 输出 27, 3引用类型
const a = EUDArray(1);a[0] = 27;const b = a;b[0] = 3;println("{}, {}", a[0], b[0]); // 输出 3, 3引用类型传递的是值在内存中的位置,而值类型传递的是值本身。
-
运行时字符串
可以使用 GetMapStringAddr 获取地图字符串在内存中的地址,再使用内存函数修改它的内容(无法改变字符串尺寸)。
const buf_index = GetStringIndex(py_str(" ") * 64); // 一个长度为 64 全是空格的字符串const buf_addr = GetMapStringAddr(buf_index);dbstr_print(buf_addr, "字符串 1");DisplayText(buf_index);dbstr_print(buf_addr, "字符串 ", 2);DisplayText(buf_index);使用引用类型操作内存缓冲区实现(无法改变字符串尺寸)。
const buf = StringBuffer(64);buf.insertf(0, "字符串 {}", 1);buf.append("字符串 2");buf.DisplayAt(6); // 输出显示到屏幕文字第六行DisplayText(buf.StringIndex);使用 Db 内存数据(无法改变字符串尺寸)。
const buf_addr = Db(64);dbstr_print(buf_addr, "字符串 1");simpleprint(buf_addr);dbstr_print(buf_addr, "字符串 ", 2);simpleprint(buf_addr); -
静态或动态说明
所有变量(不论全局还是局部)都会静态分配到固定内存空间。局部变量也是静态变量,没有变量栈,每个变量都是持久化的。如下代码:
function x() {var y;y++;return y;}与如下代码大致等效(区别在于,上面代码中 y 的作用域在 x 函数内,而下面代码中的 x__y 变量作用域是包含 x 函数的整个模块):
var x__y;function x() {x__y++;return x__y;}它们的结果都是
var a = x(); // returns 1var b = x(); // returns 2var c = x(); // returns 3warning 只有没有给局部变量初始化值时,它才会有静态特性。如果你使用
var y = 0;声明变量时给它一个初始值,则每次调用 x 时,y 的值都是 0。euddraft 对变量有初始值和无初始值的处理行为不同,因此可能导致混淆。不要依赖该特性写代码,最好给所有变量都明确指定初始值。如果希望一个变量具有静态特性,可使用 static 关键字显式声明它,并明确给它指定一个初始值,哪怕它是 0。该特性对于对象也是一样的。所有对象无论是局部还是全局,都有固定的内存空间。例如,参考以下代码:
function main() {const X = EUDArray(10);for (var i = 0 ; i < 10 ; i++) {X[i] = EUDArray(10);}}你可能认为我们为 X 的每个单元格分配了一个单独的
EUDArray(10)实例,但这段代码并不是这样执行的。上面的代码等同于:const _t0 = EUDArray(10); // Even intermediate values are staticconst _t1 = EUDArray(10); // Even intermediate values are staticfunction main() {const X = _t0;for (var i = 0 ; i < 10 ; i++) {X[i] = _t1;}}这不是你可能预料或期待的结果:X 数组的所有值都指向同一个
_t1EUDArray。
而且,EUDArray没有X[i] = EUDArray.alloc(10)这种用法。尽管我们无法在运行时动态创建数组,但仍然可以静态分配缓冲区,并在运行时动态使用它们。参考以下代码:
function onPluginStart() {const X = EUDArray(10);foreach (i : py_range(5)) {X[i] = EUDArray(10);}const X_2 = EUDArray.cast(X[2]);X_2[3] = 4;println("{}", X_2[3]);}上面的代码等同于
function onPluginStart() {const X = EUDArray(10);X[0] = EUDArray(10);X[1] = EUDArray(10);X[2] = EUDArray(10);X[3] = EUDArray(10);X[4] = EUDArray(10);const X_2 = EUDArray.cast(X[2]);X_2[3] = 4;println("{}", X_2[3]);}以上内容参考:https://github.com/phu54321/euddraft/wiki/9B.-Appendix---Static-or-Dynamic-instantiation
-
const 和 var 的说明
const 的实质是声明一个 Python 层(编译期)的变量,而不是地图运行时变量;声明对象时,它存储的是所引用对象的运行时地址。
var 的实质是声明一个 EUDVariable 对象引用的语法糖,EUDVariable 是地图运行时的一种变量。
它与 const 的不同在于,它会在编译期将对其赋值的操作=编译成 Python 层的左位移符号<<。运行时类型的左位移符号通常被重载为对运行时对象存储的值进行更改,而语法层 const 不允许在声明初始值后再使用赋值符号=。若用 var 声明一个 EUDArray 数组,实质上是声明了一个 EUDVariable 类型的变量,里面存储着一个 EUDArray 对象的地址。对这个变量使用下标运算会抛出语法错误,因为它是 EUDVariable,而不是 EUDArray。
var arr = EUDArray(10);arr[0] = 1; // 语法错误!arr 是一个 EUDVariable,不支持取下标运算符,它内部保存着一个 EUDArray 对象地址const arrt = EUDArray.cast(arr); // 可以这么用,将 arr 的值包装成一个 EUDArray 对象引用arrt[0] = 1;以下示例说明 var 和 const 的关系,两份代码等效:
var 变量名字 = 3;变量名字 = 4;变量名字 += 5;println("{}", 变量名字);const 变量名字 = EUDVariable(3);变量名字.__lshift__(4);变量名字.__iadd__(5);println("{}", 变量名字);在 epScript 中可以进行编译期交互:
var v = 100;const i = 10;const s = py_str("Location ") + py_str(i);py_print(py_str("编译到了 const s = {} 的位置").format(s));py_print("变量 v 的实质是一个 ", v, " 对象");