当使用列表型变量作为参数时,系统函数callscripticon的可选参数byvalue决定参数是以引用方式传递,还是以传值方式传递。该参数的默认值是false,表示参数以引用方式传递,图标变量args相当于参数变量的别名,这意味着脚本函数可以修改参数变量的值。当参数byvalue的值为true时,authorware将参数变量的值复制到图标变量args中,脚本函数中所有对图标变量args的操作不会影响到参数变量本身。
系统函数callscripticon的另一个可选参数owner用于设置脚本函数的拥有者。在默认情况下,调用脚本函数的运算设计图标(即执行系统函数callscripticon的运算设计图标)就是拥有者,这意味着由脚本函数绘制到【演示窗口中】的显示对象都属于该运算设计图标,通过【擦除】设计图标(或其他设计图标的自动擦除选项以及系统函数eraseicon)擦除该设计图标就可以擦除由脚本函数绘制到屏幕中的对象。例如在图3-41所示的流程中,执行eraseicon(iconid@"call box")可以将窗口中显示的所有矩形擦除,而执行eraseicon(iconid@"box")则没有任何效果。如果在调用脚本函数时总是将参数owner设置为true,即以callscripticon(@"box",,,1)方式调用脚本函数,就将脚本函数的拥有者设置为对应的【脚本函数】设计图标“box”,此时再执行eraseicon(iconid@"box"),就可以擦除【演示】窗口中所有由脚本函数box绘制的矩形。
由调用者作为拥有者,就意味着由脚本函数输出到演示窗口中的内容可以被分别处理,不仅可以用擦除设计图标进行单独擦除,还可以用移动设计图标进行单独移动,甚至单独响应用户的操作。
脚本函数之间可以相互调用,也可以进行递归调用(调用其自身)。这种相互调用存在一个最大次数的限制,因为系统函数callscripticon的参数栈是有限的,每个参数将会在栈中占据一定的位置,使用args参数将使递归调用的最大次数减半。同时使用4个参数将使递归调用的最大次数减少到1/4。当达到栈的最大容量后,系统变量evalstatus和evalmessage将包含对应的出错信息。递归调用的最大嵌套深度是12。
脚本函数的递归调用不是真正的递归调用,真正的递归调用意味着每层函数调用中所有的变量都是局部变量,而authorware中只存在全局变量,因此当函数调用自身时,当前层中变量的值会被下一层调用中变量的值所覆盖。所以,当进行递归调用时,必须建立和维护脚本函数的变量堆栈,最简便的方法是创建一个自动增长的列表来保存每层函数调用中的变量值,当每层函数调用返回时缩短列表的长度。如果在脚本函数中需要同时跟踪多个变量的值,那么就要使用多维列表。
authorware没有提供计算整数阶乘的系统函数。以下是一个由aws编写的脚本函数recursion,通过递归调用实现整数(12以内)的阶乘。
-- nesting@"recursion"用于记录当前递归调用的嵌套深度
nesting@"recursion":=nesting@"recursion"+1
--由线性列表linearlist@"recursion"形成变量堆栈,保存每层调用中的args@"recursion"
--参数值。因为在下一层调用中,变量args@"recursion"值会发生变化
addlinear(linearlist@"recursion", args@"recursion", nesting@"recursion")
if args@"recursion">1 then
--n!=(n-1)!×n 在此不能直接使用变量args@"recursion",因为在后续的调用中它的值
--被改变。只能使用变量堆栈中保存的值。
result@"recursion":=callscripticon(@"recursion",args@"recursion"-1)* ﹁
linearlist@"recursion"[nesting@"recursion"]
else
result@"recursion":=1
end if
--退出本层递归过程之前,删除无用的列表元素(相当于将变量出栈)
deleteatindex(linearlist@"recursion", nesting@"recursion")
nesting@"recursion":=nesting@"recursion"-1
位于chapter03文件夹下的范例程序recursion.a7p提供了脚本函数recursion。如图3-46所示,读者可以在文本输入框中输入1至12之内的整数,回车之后就可以看到运算结果。
图3-46 范例程序recursion.a7p
现在以函数调用callscripticon(@"recursion",4)为例,通过参数和堆栈列表值的变化分析递归调用过程:
result:= callscripticon(@"recursion",4)
进入第1层
linearlist@"recursion" [4]
nesting@"recursion" 1
args@"recursion" 4
result@"recursion" 0
进入第2层
linearlist@"recursion" [4, 3]
nesting@"recursion" 2
args@"recursion" 3
result@"recursion" 0
进入第3层
linearlist@"recursion" [4, 3, 2]
nesting@"recursion" 3
args@"recursion" 2
result@"recursion" 0
进入第4层
linearlist@"recursion" [4, 3, 2, 1]
nesting@"recursion" 4
args@"recursion" 1
result@"recursion" 1
返回第3层
linearlist@"recursion" [4, 3, 2]
nesting@"recursion" 3
args@"recursion" 1
result@"recursion" 2
返回第2层
linearlist@"recursion" [4, 3]
nesting@"recursion" 2
args@"recursion" 1
result@"recursion" 6
返回第1层
linearlist@"recursion" [4]
nesting@"recursion" 1
args@"recursion" 1
result@"recursion" 24
最后返回计算结果24。
由于使用了堆栈,不要在脚本函数中使用goto()函数。可在正常退出脚本函数之后,再根据需要跳转到其他设计图标。
创建多个脚本函数之后,就需要加强对脚本函数的管理,为每个脚本函数进行必要的说明,如图3-47所示,这样就可以保证无论何时面对这些脚本函数,都能够保持清醒。同时还需要一种手段,可以方便地向设计图标中添加需要使用的脚本函数。通过为【脚本函数】设计图标添加描述信息可以实现上述目的。
在函数面板中【category】下拉列表框中选择script icons类,在其中可以找到当前程序中被创建的所有脚本函数,在函数列表中选择需要添加描述信息的脚本函数后,就可以向【description】文本框中输入脚本函数的使用说明。注意将第一段内容保持为脚本函数的完整使用语法(请参阅图3-47)。
此后在函数列表中双击需要使用的脚本函数,或者单击函数面板的【paste】按钮,就可以将该函数的使用语法粘贴到【运算】窗口中当前光标所在位置处或者设计图标属性检查器中,就像使用系统函数一样方便,如图3-48所示。
图3-47 为【脚本函数】设计图标添加描述信息
图3-48 粘贴脚本函数
由于脚本函数只能通过系统函数callscripticon进行调用,所以原则上【脚本函数】设计图标可以放在流程线上的任意位置而不会影响程序的正常运行。但是在这里还是建议将所有的【脚本函数】设计图标集中放置在一起,以便于日后进行维护,如图3-49所示。
图3-49 集中管理脚本函数
脚本函数可以存储在外部文本文件中。包含脚本函数的文本文件可以位于本地驱动器,也可以位于网络中(通过url指定地址)。将脚本函数存储在外部文本文件中的真正意义在于,不同的程序文件可以共享同一份脚本函数,同时便于进行程序发行后的代码维护。设计人员可以将最有可能发生改动的代码以外部脚本函数的形式保存在程序文件外部。
位于文本文件中的外部脚本函数必须通过系统函数callscriptfile()进行调用,其使用语法为:
result:= callscriptfile("filename" [,args] [,byvalue])
与callscripticon()函数相比,callscriptfile()函数使用文本文件的路径和名称来代替【脚本函数】设计图标的名称。
本书3.5.2节中介绍的字符串处理脚本函数都已经被转换为外部脚本函数,分别存放在chapter03/scriptfiles文件夹下的6个文本文件之中。chapter03文件夹下的范例程序scriptfile.a7p示范了如何使用这些外部脚本函数。
以下是外部脚本函数sortchar.txt中的代码(观察与内部脚本函数sortchar之间的区别):
charlist@iconid:=[]
result@iconid:=""
if listcount(args@iconid)>1 then
ascending@iconid:=args@(iconid)[2]
else if listcount(args@iconid)=1 then
ascending@iconid:=true
else
args@iconid:=list(args@iconid)
ascending@iconid:=true
end if
repeat with i@iconid:=1 to charcount(args@(iconid)[1])
addlinear(charlist@iconid, substr(args@(iconid)[1], i@iconid, i@iconid))
end repeat
sortbyvalue(charlist@iconid,ascending@iconid)
repeat with i@iconid:=1 to charcount(args@(iconid)[1])
result@iconid:=result@iconid^charlist@(iconid)[i@iconid]
end repeat
将内部脚本函数转换为外部脚本函数的要点在于:
(1)在使用各种图标变量时,以@iconid代替@"sortchar"。忽略设计图标的名称后,就可以在任意运算设计图标中调用存储于程序文件外部的脚本函数。
(2)对于列表型图标变量(例如args和charlist),由于下标运算符的优先级高于引用运算符,因此必须对iconid使用括号,以类似于args@(iconid)[下标]的方式访问列表中的元素。
(3)必须为调用外部脚本函数的运算设计图标创建函数中使用的所有图标变量。对于脚本函数sortchar而言,包含args,result,i,charlist和ascending,同时还要在程序文件范围内创建全局变量args,result,i,charlist和ascending,否则在调用脚本函数时会出现“variable is not defined.”的错误提示信息。脚本函数中使用的全局变量也必须在程序文件范围内重新定义。
外部脚本函数sortchar.txt的调用方法是:
result:= callscriptfile(filelocation^"scriptfiles//sortchar.txt", [entrytext,0])
将脚本函数存储在外部文本文件中可能会带来一些安全问题:任何人都可以轻易地通过任意一种文本文件编辑工具查看外部脚本函数的内容,从而了解程序运行过程的细节,或者恶意修改代码,加入一些额外的指令。如果能够将外部脚本函数进行加密,就能够从很大程度上防止出现上述情况。
字符串脚本函数就是存储有脚本函数的字符串。设计人员可以将字符串脚本函数加密后存储于文本文件之中,在需要使用这些函数时,将加密的函数代码读入字符串变量并进行解密,还原为字符串脚本函数,然后通过系统函数callscriptstring()对字符串脚本函数加以调用。
字符串脚本函数与其他类型脚本函数的主要区别在于,字符串脚本函数中只能使用全局变量。因此必须在程序文件范围内创建字符串脚本函数中用到的所有变量,否则在调用字符串脚本函数时会出现“variable is not defined.”的错误提示。
以下是字符串脚本函数sortchar所包含的代码(观察与内部脚本函数sortchar存在的区别):
charlist:=[]
result:=""
if listcount(args)>1 then
ascending:=args[2]
else if listcount(args)=1 then
ascending:=true
else
args:=list(args)
ascending:=true
end if
repeat with i:=1 to charcount(args[1])
addlinear(charlist, substr(args[1], i, i))
end repeat
sortbyvalue(charlist,ascending)
repeat with i:=1 to charcount(args[1])
result:=result^charlist[i]
end repeat
字符串脚本函数sortchar的调用方法是:
script:=readextfile("sortchar.txt")
args:=["authorware 7.0 is coming", 0]
callscriptstring(script ,args)
脚本函数的返回值存储在全局变量result之中。
本书3.5.2节中介绍的字符串处理脚本函数都已经被转换为字符串脚本函数,分别存放在chapter03/scriptstring文件夹下的6个文本文件之中。chapter03文件夹下的范例程序scriptstring.a7p示范了如何使用这些字符串脚本函数。
本书3.5.2节中介绍的encode脚本函数就是一个很好的加密函数,利用它就可以将字符串脚本函数包含的代码进行加密。例如通过以下过程,就可以将存储于wordreverse.txt文件之中的字符串脚本函数进行加密,并保存在wordreverse_encrypted.txt文件之中。
script:=readextfile("wordreverse.txt")
encryptedscript:=callscripticon(@"encode",script)
writeextfile("wordreverse_encrypted.txt", encryptedscript)
打开wordreverse_encrypted.txt文件,只能看到一些杂乱无章的数字,如图3-50所示。但是可以通过以下过程解密并调用字符串脚本函数。
script:=callscripticon(@"decode", readextfile("wordreverse_encrypted.txt"))
args:="authorware 7.0 is coming"
callscriptstring(script, args)
图3-50 加密后的字符串脚本函数
本书3.5.2节中介绍的字符串处理脚本函数都已经被转换为加密的字符串脚本函数,分别存放在chapter03/encryptedscript文件夹下的6个文本文件中。chapter03文件夹下的范例程序scriptstringencrypted.a7p示范了如何使用这些加密的字符串脚本函数。
新闻热点
疑难解答