本帖最后由 脑的研发记录 于 2023-11-7 13:23 编辑
“数据结构课作业笔记”#004-数组-简单排列的变量和它们必然命运(下)
所用编辑器版本0.25.0.4
目录总览:
零.再前情提要
壹.第一个自由数组
贰.所需语法 (代码如本节图示)
叁.应用说明
肆.相关参考链接 (返回主站)
伍.专有名词的真相(类比之后,默认语境的调整,专业名词对照说明)
陆.告一段落,回顾语
零.再前情提要
数组一次性赋值,实现了按计划传送。不过数组层面就只有读取数据,一切按既定计划进行。但是遇到临场变动,修改计划内容,想在第一次填充的数据的时候就可以符合新的需求就遇到了困难,代码报错说变量不存在,写入数据总是要一次性赋值然后再修改,精力消耗感觉大,膈应。这次补充数组的修改,完成第一个具有特殊身份的数组。
壹.第一个自由数组
不多说,上代码,新建脚本,复制粘贴跑就完事。
interface see {
a: number;
check: Vector;
}
class seetest {
public a: number;
public check: Vector;
}
@Core.Class
export default class add extends Core.Script {
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
// var arr_names:see[] = new Array(4)
// var i = 0;
// arr_names[0].a = 2
// console.log(arr_names.a);
// 日志提示警告:数组每一项的成员a不存在,而且也确实无法输出a。
// 原因是see类型不是默认直接直接转化的类型,
// 需要Array<see>类型转换,才能让数组识别
// var arr_names: Array<see> = new Array(4)
// var i = 0;
// arr_names[0].a = 2
// console.log(arr_names.a);
// 日志依然提示警告:数组每一项的成员仍然不存在,而且也确实无法输出a。
// 原因是see类型是interface类别的,不是默认直接直接转化的类型,
// 需要Array<see>类型转换,才能让数组识别
// var arr_names: Array<seetest> = new Array(4)
// var i = 0;
// var m = new seetest();
// m.a = 1;
// m.check = new Vector(100, 0, 0);
// console.log("m=" + m.a);
// arr_names[0] = m;
// console.log(arr_names[0].a);
var i = 0;
var arr_names: Array<seetest> = new Array(6)
for (i = 0; i < arr_names.length; i++) {
arr_names[i]= new seetest();
arr_names[i].a = i * 2;
arr_names[i].check = new Vector(500 * i, 0, 300);
console.log(arr_names[i].check.x)
}
i = 5;
// arr_names[2].check = new Vector(100, 0, 1000);
var chara: Gameplay.Character;
setTimeout(() => {
chara = Gameplay.getCurrentPlayer().character;
chara.worldLocation = arr_names[i].check;
var timer = setInterval(() => {
if (i < 0) {
// 如果i小于0,就清除定时器
clearInterval(timer);
}
chara.worldLocation = arr_names[i].check;
console.log(arr_names[i].check.x)
console.log(i);
i--;
// 每500毫秒i减少1
}, 500);
}, 2000);
}
}
数组不用预先一个一个敲数字进行赋值,实现了自动填充。然后再按新的计划控制角色闪现。
绿色的大块字符是数组编写过程中遇到的bug,是在穷举到成功满足需求的之后才明白代码输出失败的问题。
代码拆解
对数组第一次赋值,人称“初始化”。
var i = 0;
var arr_names: Array<seetest> = new Array(6)
for (i = 0; i < arr_names.length; i++) {
arr_names[i]= new seetest();
arr_names[i].a = i * 2;
arr_names[i].check = new Vector(500 * i, 0, 300);
console.log(arr_names[i].check.x)
}
先声明数组,类型是seetest, 长度是6
然后数组的每一项都新建了一个seetest的新变量,
每一项的名叫"a"的变量成员都存储了一个数字,
每一项的名叫“check”的变量成员都存储了一个位置信息。
然后打印每一项的位置信息的x坐标。
这也是一种初始化,其实这俩都相当于产生铭牌,每个铭牌给一个房子。只不过下面的代码是先一下子产生所需的铭牌,然后再把铭牌挂到房子上
var i = 0;
// 产生6个铭牌
var arr_names: Array<seetest> = new Array(6)
// 对每一个铭牌分配一个seetest户型的房子
for (i = 0; i < arr_names.length; i++) {
arr_names[i]= new seetest();
}
// 每个铭牌指定的房子里安排进家具
for(i=0;i<arr_names.length;i++){
arr_names[i].a = i * 2;
arr_names[i].check = new Vector(500 * i, 0, 300);
console.log(arr_names[i].check.x)
}
这样之后,可以把用函数打包一些句子,变成这样,就可以实现存入数据,in一次就存入一项seetest格式的数据,in两次就存入两项seetest格式的数据。
// 对每一个铭牌分配一个seetest户型的房子
for (this.i = 0; this.i < this.arr_names.length; this.i++) {
this.arr_names[this.i] = new seetest();
}
// 每个铭牌指定的房子里安排进家具
for (this.i = 0; this.i < this.arr_names.length;) {
this.in(new Vector(500 * this.i, 0, 300), this.i * 2);
}
其中的in函数样子如下
public in(check: Vector, a: number) {
this.arr_names[this.i].a = a
this.arr_names[this.i].check = check;
console.log(this.arr_names[this.i].check.x)
this.i++;
}
然后想着输入已经打包了,封装一个输出,注意到代码
var chara: Gameplay.Character;
setTimeout(() => {
chara = Gameplay.getCurrentPlayer().character;
chara.worldLocation = arr_names[i].check;
var timer = setInterval(() => {
if (i < 0) {
// 如果i小于0,就清除定时器
clearInterval(timer);
}
chara.worldLocation = arr_names[i].check;
console.log(arr_names[i].check.x)
console.log(i);
i--;
// 每500毫秒i减少1
}, 500);
}, 2000);
找不同,对小学生来说有点幼稚,对大学生来说刚刚好。
var chara: Gameplay.Character;
setTimeout(() => {
chara = Gameplay.getCurrentPlayer().character;
// chara.worldLocation = this.arr_names[this.i].check;
chara.worldLocation = this.out().check;
var timer = setInterval(() => {
if (this.i < 0) {
// 如果i小于0,就清除定时器
clearInterval(timer);
}
// chara.worldLocation = this.arr_names[this.i].check;
// console.log(this.arr_names[this.i].check.x)
// console.log(this.i);
// this.i--;
// 每500毫秒i减少1
// var c: seetest = new seetest();
// c = this.out();
// chara.worldLocation = c.check;
// console.log(c.a);
chara.worldLocation = this.out().check;
}, 500);
}, 2000);
out 函数具体如下
public out(): seetest {
this.i--;
var now: seetest = new seetest();
now = this.arr_names[this.i];
return now;
}
最后来一个代码全景,新建脚本,复制粘贴就能跑。
最终成果
class seetest {
public a: number;
public check: Vector;
}
@Core.Class
export default class add extends Core.Script {
public arr_names: Array<seetest> = new Array(6)
public i: number = 0;
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
// 对每一个铭牌分配一个seetest户型的房子
for (this.i = 0; this.i < this.arr_names.length; this.i++) {
this.arr_names[this.i] = new seetest();
}
// 每个铭牌指定的房子里安排进家具
for (this.i = 0; this.i < this.arr_names.length;) {
this.in(new Vector(500 * this.i, 0, 300), this.i * 2);
}
var chara: Gameplay.Character;
setTimeout(() => {
chara = Gameplay.getCurrentPlayer().character;
// chara.worldLocation = this.arr_names[this.i].check;
chara.worldLocation = this.out().check;
var timer = setInterval(() => {
if (this.i < 0) {
// 如果i小于0,就清除定时器
clearInterval(timer);
}
chara.worldLocation = this.out().check;
}, 500);
}, 2000);
}
public in(check: Vector, a: number) {
this.arr_names[this.i].a = a
this.arr_names[this.i].check = check;
console.log(this.arr_names[this.i].check.x)
this.i++;
}
public out(): seetest {
this.i--;
var now: seetest = new seetest();
now = this.arr_names[this.i];
return now;
}
}
对比前一节代码
interface have {
i: number;
check: Vector;
}
// 说明一个数据类型,如代码所示,这个叫“have"的类型
// 有一个叫“i“的变量,它的类型是numebr
// 还有一个叫“check"的变量,它的类型是Vector
@Core.Class
export default class stack extends Core.Script {
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
var a: have[] = [
{ i: 0, check: new Vector(500, 0, 100) },
{ i: 1, check: new Vector(1000, 0, 100) },
{ i: 2, check: new Vector(1500, 0, 100) },
{ i: 3, check: new Vector(2000, 0, 100) },
{ i: 4, check: new Vector(2500, 0, 100) },
{ i: 5, check: new Vector(3000, 0, 200) }
];
var i = 5;
var chara: Gameplay.Character;
setTimeout(() => {
chara = Gameplay.getCurrentPlayer().character;
chara.worldLocation = a[i].check;
var timer = setInterval(() => {
if (i < 0) {
// 如果i小于0,就清除定时器
clearInterval(timer);
}
chara.worldLocation = a[i].check;
i--;
// 每500毫秒i减少1
}, 500);
}, 2000);
}
}
代码就着文案现写现改,总得来说分成两个部分:封装和解决封装遇到的报错。
把i变量从里面提到外部,其他函数可以直接获取i,进行修改,原来传进参数修改的路线失败了。
然后封装过程中,使用的数据seetest类型的改变,从interface到class,每个数据初始化都需要new 来执行分配空间。
初始化数组的时候,数组也需要重新传入数据格式,传入数据格式的方法也变化了。
其他就按照前节的代码手动输入。
总得来说,代码修改,像从小房子装水管,从水管到简单挂起,再到管线臃肿混乱,在意料之外的报错中拿到经验,最后拆除重写,小房子才有了新的管道线路。
本质来说是代码之间的联系更准确了,准确的同时,也多了支持精准处理的变量,变量位置调整。
功能依旧是 提前写入,按计划使得角色倒退。
但是封装之后,in out的操作,使得总是处理数组一段的数据,最后产生的却先使用,最先产生的最后使用。
500 1000 1500 2000 2500 3000
输出是
3000 2500 2000 1500 1000 500
倒着输出了
但是如果在输出3000之后,输出2500之前,又输入一个数字3500
下次输出的就是3500
输出3500之后,再输出2500之前,又输入4000,
下次输出的就是4000。
进进出出,数字还是数字,代码层面确实是这样就结束了。
贰.参考语法
TypeScript 类 | 菜鸟教程 (runoob.com)
别看太久,我觉得代码跑起来就行,看了不能立即敲代码改参数的还不是这个阶段的事情。
叁.应用说明
数字进出,可以赋予数字意义,数字代表,数字进出,方向进出,走迷宫,走到死路,回滚步数,倒回一步换另一个方向,再探测,不通,回到原来方向,换另另一个方向再探测,不通,回到原来方向,换另另另外一个方向探测。穷举完倒数第一步的四种方向,再穷举倒数第二步,换另一个方向上的倒数第一步,再穷举四个方向,如果不通,就在倒回第二步,换再穷举倒数第三步。
以上操作的高大上名称为“深度优先寻路”。“一直往下走,走不通回头,换条路再走,直到无路可走”。
以上操作的代码层面的名称——栈,后进先出。
游戏方面,走迷宫,回退到上一个分支。
肆.相关参考链接 (返回主站)
普通大学生“数据结构”课作业笔记#000-目录-逻辑结构与存储结构 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
伍.专有名词的真相(拓展视野,饭后闲聊)
不过是在默认语境(直觉)下,为了一语中的,才产生的专有名词。
成员变量(简称 “成员”)(member variable)
类(class)
指针(pointer)
地址(address)
分配内存(函数new)
指针数组(Array of pointers)
只要铭牌挂在房子上,就能修改数组里面的具体变量
铭牌就是变量名,房子就是一块地,就是内存,
铭牌刻上的就是房子的地址,
变量存着的是内存的地址,
底层支持:CPU找内存
用腿跑路,拿着铭牌里的名字比对去找房子,如何奔跑按图索骥到达房子不多说,
用CPU找,拿着数组里的地址比对去找内存,如何遍历按图索骥到达内存不多说。
底层优化本质:让CPU找更少次数
陆.告一段落,回顾语
文案现写,代码现写,注释无数,回滚无数,踩坑无数。
这是量大的预兆,这是标记的提醒。
标记换个顺序,代码就得天翻地覆一次。
教程不好写。
现在才知道教材原来是势力平衡的结果:省略一步,穷举十次,省略两步,穷举百次。不省略,细节多如牛毛。
从数组到栈,中间我们老师讲了其他的数据,铺垫了如数据类型,如抽象数据对象,数据项,数据元素,队列,顺序表,链表等等。
我觉得还是离代码有点远,或者是从一个已经预料到结果的视角开始入手,就会跳过了自己的那部分随意发挥才华的故事。
等着,哪天咱也出点靠谱教材,第一人称,全过程记录,平地起高楼。
|