数字黑洞(下):四位数的黑洞
在上一期中我们展示了三位黑洞数的求解过程,其中的关键技术是数字的排序及位置交换。本期文章继续讨论有关数字黑洞的问题,展示四位黑洞数的求解过程。
一、用户界面
在App Inventor开发环境中打开上一期创建的项目——数字黑洞1,在项目菜单中选择“另存项目”,将项目名称修改为“数字黑洞2”,如图 1所示,保留项目中的全部组件。
图1 将“数字黑洞1”项目另存为“数字黑洞2”
二、技术要点分析
在求解三位黑洞数时,我们用局部变量(数1、数2、数3)保存三位数的三个数字,两两比较数的大小,根据比较结果决定是否将两个数交换位置,这样的处理过程需要3次比较,要编写3组共9条指令,如图 2所示,每组指令的格式相同。如果我们沿着同样的思路,对四个数字进行排序,那么就需要进行6次比较,编写6组共18条指令,同样每组指令的格式相同。
图2 三个条件语句中的代码格式相同
作为一个开发者,我们不能允许程序中出现如此多的重复代码,这些重复代码不仅让程序变得臃肿,而且当这部分代码需要修改时,极易给程序带来错误,例如由于疏忽忘记了修改其中的部分代码。
解决这一问题的关键技术有两点——列表与循环。将四位数的四个数字转换为列表,利用循环语句,对列表项进行两两比较,通过交换列表项的位置,来实现数字的排序。这也是解决大量数字排序问题的通用方法,例如,对班级考试成绩进行排序。
三、编写程序
将开发工具切换到编程视图,保留项目中的部分代码,将最大值、最小值过程删除,如图 3所示。
图3 保留项目中的部分代码
1、定义过程——数字转列表、列表转数字
为了利用列表和循环语句简化排序代码,首先需要将数字转化为列表,待排序完成后,再将列表转为数字,这样才能进行减法运算。代码如图 4及图 5所示。
图4 定义过程——数字转列表
图5 定义过程——列表转数字
2、定义过程——极值
在数学语言中,最大值、最小值统称为“极值”,这里定义一个有返回值的过程——极值,来取代最大值、最小值两个过程,代码如图 6所示。
图6 定义过程——极值
极值过程里使用了双层循环,外层循环变量“外层序号”的取值为1~3,内层循环变量“内层序号”的起始值为(外层序号+1),终止值为4,所有指令都包含在内层循环中。首先取出数字列表中的第1项,与后面3项进行两两比较,并交换位置,当第1次内层循环(共执行3次)结束时,列表中的第1项为四个数字中的最大数或最小数。接下来取出列表中的第2项,与后面两项进行比较并交换位置,当第2次内层循环(共执行2次)结束时,列表中的第2项为后三位数中的最大值或最小值,以此类推,当两层循环全部结束时,数字列表的排序完成。
注意:在交换两个列表项的位置时,同样需要一个临时变量,先将前数保存在临时变量中,然后用后数替换前数所对应的列表项,最后再用临时变量替换后数所对应的列表项。
极值过程里还使用了有返回值的条件语句,如果参数“极大”=真,则交换位置的条件为“前数<后数”,并返回最大值列表,否则条件为“前数>后数”,并返回极小值列表。
3、改造过程——求差运算
首先将求差运算的“三位数”参数修改为“数字列表”,通过调用极值过程,求得四位数的最大值和最小值,再求两者的差值并拼接输出字串,比较差值与黑洞数(全局变量),如果差值不等于黑洞数,则继续调用求差运算过程(递归调用),直到差值与黑洞数相等,然后将输出字串显示在标签中。代码如图 7所示。
图7 改造之后的求差运算过程
4、改造求解按钮的事件处理程序
在求差运算过程中,第一个参数的数据类型由数字改成了列表,因此,在求解按钮的点击事件中,在调用求差运算过程时,要为过程提供列表类型的参数,此处利用数字转列表过程,将数字变为数字列表,代码如图 8所示。
图8 改造之后的点击事件处理程序
对上述代码进行测试,测试结果如图 9所示。
图9 测试:四位黑洞数的求解过程
四、讨论
读者可能有疑问:图 7的求差运算过程里,在求最大值时,用“复制列表+数字列表”块作为极值过程的参数,而在求最小值时,则直接用“数字列表”作为参数,为什么会有这样的差别呢?我们先来做一个实验,将求差运算过程修改一下,代码及测试结果如图 10所示,最大值与最小值都成了最小值,这是什么原因呢?
图10 去掉“复制列表”块时的测试结果
原因在于列表类型数据的存储方式。简单类型的变量,如黑洞数,其中保存的是具体的数,如6193,而列表类型的变量,如参数“数字列表”(参数等同于变量),其中保存的不是具体的数据,而是数据存放的地址。举例来说,假设列表A、列表B为变量名,经过下列操作:
1. 设列表A = (6 1 9 3)
2. 设列表B = 列表A
3. 设列表B = (9 6 3 1)
此时查看列表A的值,会发现列表A也变成了(9 6 3 1)。
也就是说,第2步操作并没有真正生成一个变量,只是为“列表A”贴了一个“列表B”的标签,它们指向的是同一个数据存储区域,因此,对列表B的改写,同时也发生在列表A之上。
回到求差运算过程里,求差运算的参数“数字列表”、过程里的局部变量最大值、最小值,这三者指向的是同一个存储区域,而“复制列表”块所起的作用,是另外开辟一个存储区域,将“数字列表”的值复制到新区域中,因此求得的最大值保存在新区域中,而最小值其实是保存在参数“数字列表”所在的区域中。这就是使用“复制列表”块的缘由。
注:本文已正式发表于《爱上机器人》杂志总第4期(2019.01)。