在之前的一篇文章中,我展示了 Rexx 脚本语言既强大又易于使用。它使用特定的技术来协调这两个通常被认为相互冲突的目标。
本文将引导您完成两个示例 Rexx 脚本,以便您了解该语言。 Rexx 声称功能强大且易于使用。
Rexx 脚本示例
LISP 编程语言以过度使用括号而闻名。程序员要确保它们都正确匹配可能是一个真正的挑战。
这个简短的脚本从用户那里读取一行 LISP 代码,并确定输入中的括号是否正确匹配。 如果括号没有正确平衡,程序将显示语法错误。
以下是与程序的三个示例交互。 在第一个示例中,我输入的 LISP 代码被正确键入。 但接下来的两个包含 Rexx 脚本识别出的不匹配的括号
Enter a line to analyze:
(SECOND (LAMBDA (LIS) (FIRST (CDR LIS)) ))
Parentheses are balanced
Enter a line to analyze:
((EQSTR (CAR LIS1) (CAR LIS2))
Syntax error: too many left parens, not balanced
Enter a line to analyze:
(EQSTR (CAR LIS1) CAR LIS2))
Syntax error: right paren before or without left paren
这是 Rexx 程序
counter = 0 /* counts parentheses */
say 'Enter a line to analyze:' /* prompts user for input */
pull input_string /* reads line of user input */
length_of_string = length(input_string)
/* process each character of the input line, one at a time */
do j = 1 to length_of_string while counter >= 0
character = substr(input_string,j,1)
if character = '(' then counter = counter + 1
if character = ')' then counter = counter - 1
end
/* display the appropriate message to the user */
if counter = 0 then
say 'Parentheses are balanced'
else if counter < 0 then
say 'Syntax error: right paren before or without left paren'
else
say 'Syntax error: too many left parens, not balanced'
首先,程序提示用户使用 say
指令输入一行输入。然后它使用 pull
指令读取它。
say
和 pull
指令用于对话式输入和输出,或与用户的直接交互。 Rexx 还支持面向字符和面向行或记录的 I/O。
接下来,脚本使用 length
函数将输入行的长度放入变量 length_of_string
中。
do
循环一次处理来自输入行的每个字符。 每次遇到左括号时,它都会递增 counter
,每次识别到右括号时,它都会递减 counter
。
如果在处理完整个输入行后 counter
最终为零,则程序知道输入行中的任何括号都正确匹配。 如果在处理后 counter
不为 0,则输入行具有不匹配的括号。
最终的 if
语句向用户显示正确的消息。 可以根据个人喜好以任何数量的样式编写这些 if
语句。 (主要要求是,无论何时在一个分支中编写多个语句,它们都必须包含在 do...end
组中。)
此程序表明 Rexx 是自由格式和不区分大小写的。 它不依赖于保留字,因此您可以自由使用像 counter
或 character
这样的常用词来表示变量。
Rexx 施加的一个关键要求是任何函数必须紧跟左括号。 程序中的示例是 length
和 substr
函数。 在函数名称和随后的括号之间放置一个空格,Rexx 将无法识别该函数。
除了像这些之外的少数最低要求之外,Rexx 对程序员的语法、特殊字符或限制性编码规则的要求很少。
Rexx 程序看起来和读起来像伪代码。 这使得它们相对容易阅读和使用。
Rexx 脚本的真实示例
这是一个来自现实世界的程序
Rexx 用户 Les Koehler 有一个遗留的会计程序,该程序将手中的会计记录与供应商每天发送给他的会计记录进行匹配。 该遗留程序每天运行几个小时来处理 25,000 条记录。 它采用顺序的“遍历列表”技术来匹配一组记录与另一组记录。
Les 用 Rexx 脚本替换了该遗留程序。 Rexx 脚本通过使用关联数组来执行匹配
/* Create an associative array reflecting */
/* the values in the first list of names */
/* by Les Koehler */
flag. = 0 /* Create array, set all items to 0 */
do a = 1 to list_a.0 /* Process all existing records */
aa = strip(list_a.a) /* Strip preceding/trailing blanks */
flag.aa = 1 /* Mark each record with a 1 */
end
/* Try to match names in the second list */
/* against those in the associative array */
m = 0 /* M counts number of missing names */
do b = 1 to list_b.0 /* Look for matching name from LIST_B */
bb = strip(list_b.b) /* Put LIST_B name into variable BB */
if \ flag.bb then do /* If name isn't in FLAG array */
m = m+1 /* add 1 to count of missing names */
missing.m = bb /* add missing name to MISSING array */
end
end
missing.0 = m /* Save the count of unmatched names */
Les 能够将处理时间从几个小时减少到几秒钟。
第一行代码 (flag. = 0
) 创建一个名为 flag
的新数组,并将该数组中的每个元素初始化为 0
。
数组 list_a
包含所有现有的会计记录。 按照惯例,它的第一个元素 (list_a.0
) 包含数组中元素的数量。
因此,第一个 do
循环处理现有记录数组 (list_a
) 中的所有元素,并将它们中的每一个标记为存在于 flag
数组中。 语句 flag.aa = 1
将 flag
数组中可寻址的内容项标记为存在。
第二个 do
循环遍历新记录集中的每个项目,该记录集包含在名为 list_b
的数组中。
if
语句检查来自第二个记录数组的项目是否标记为存在于 flag
数组中。 如果不是,则程序会递增新会计记录列表中存在的、在旧记录列表中不存在的项目数。 并且它将缺失的项目放入 missing
数组中:missing.m = bb
。
最后的语句 (missing.0 = m
) 只是更新 missing
数组中的项目数,按照惯例存储在数组位置 0 中。
Rexx 的改进
为什么这个 Rexx 程序比它替换的遗留代码快这么多? 首先,关联数组允许直接查找新记录与旧记录。 直接访问比它替换的顺序“遍历列表”技术快得多。
其次,所有数组元素都驻留在内存中。 一旦旧的和新的会计记录文件被初始化到 Rexx 数组中,就不再需要磁盘 I/O。 磁盘 I/O 总是比内存访问慢几个数量级。
Rexx 数组会扩展到内存允许的范围。 此脚本利用了具有看似无限数量 RAM 的现代计算机,并使程序员无需管理内存。
结论
我希望这两个简单的程序已经展示了 Rexx 是多么容易阅读、编写和维护。 Rexx 旨在将编程的负担放在机器上,而不是程序员身上。 然而,由于我在本系列文章中描述的设计技术,该语言仍然具有足够的强大功能。
有关免费 Rexx 下载、工具、教程等的更多信息,请访问 RexxInfo.org。 您可以免费加入 Rexx 语言协会。
本文献给 Les Koehler 的记忆,他从 Rexx 和 Rexx 社区的早期就一直活跃。
2 条评论