VFP学习、开发漫谈 (11)
今天与大伙聊一聊编程中的分支结构和循环结构。分支结构包括“IF...ENDIF”和 “DO CASE...ENDCASE”两组命令以及 IIF() 和 ICASE() 两个函数。
这几个命令和函数在某些情况下,可以互相代替,但我一般采取如下原则:
1. 尽可能将同一层次上的多个 IF 语句改成 DO CASE 结构,因为 DO CASE 结构更清晰,更简洁。
2. 从可读性来说,尽量少用 ICASE()函数,将其改为 DO CASE 命令较好。
3. 同样,从可读性上考虑,不建议嵌套使用 IIF()函数,即:在IIF()函数内部再使用IIF()函数,可改用 IF...ELSE...ENDIF 结构
在使用 DO CASE 结构时,CASE 语句的前后顺序很重要,因为后面的 CASE 语句是在不满足前面的 CASE 条件时,才会执行。下面是一个计算调节税的代码,各 CASE 语句的顺序不能调换:
程序代码:
nAmt = 工资表.应发工资 - 3500 && 应纳税额 DO CASE CASE nAmt <= 0 && 应纳税额小于0,不纳税 nTax = 0 CASE nAmt <= 1500 && 0 - 1500,税率 3% nTax = nAmt * 0.03 CASE nAmt <= 4500 && 1500 - 4500,税率 10% nTax = (nAmt - 105) * 0.1 CASE nAmt <= 9000 && 4500 - 9000,税率 20% nTax = (nAmt - 555) * 0.2 CASE nAmt <= 35000 && 9000 - 35000,税率 25% nTax = (nAmt - 1005) * 0.25 CASE nAmt <= 55000 && 35000 - 55000,税率 30% nTax = (nAmt - 2755) * 0.3 CASE nAmt <= 80000 && 55000 - 80000,税率 35% nTax = (nAmt - 5555) * 0.35 OTHERWISE && 超过 80000,税率 45% nTax = (nAmt - 13505) * 0.45 ENDCASE
在判断复选框(CheckBox)是否被选定时,用“IF EMPTY(Check1.Value)”比用“IF Check1.Value = 0”要好。因为复选框的 Value 属性既可以是数值型,也可以是逻辑型。使用前者可适用于这两种情况,而后者仅适用于Value是数值的情况。
VFP 在处理逻辑表达式时有自己的特点。
1. 在处理“条件A AND 条件B”时,若条件A不成立,则不会再去判断条件B。例如:执行“IF 1>2 AND MESSAGEBOX('正确否?',36,'提示')=6”时,并不会弹出对话框。
2. 在处理“条件A OR 条件B”时,若条件A成立,则不会再去判断条件B。例如:执行“IF 2>1 OR MESSAGEBOX('正确否?',36,'提示')=6”时,也不会弹出对话框。
从上面的两种情况可以看出,逻辑连接符(AND,OR)两边的条件表达式的前后顺序,对程序的执行有影响。一般来说,把执行速度最快、最易判断的关系表达式放在前面。
有时候,通过将多个关系表达式用 OR 连接在一起,可以代替多个并列的 IF 语句或DO CASE 语句。例如:
IF 采购单.制单人 # gcUserName
* 禁止修改其他人的采购单
lErr = .t.
ENDIF
IF !EMPTY(采购单.审核)
* 禁止修改已审核的采购单
lErr = .t.
ENDIF
可以简化为以下语句:
* 禁止修改其他人的采购单和已审核的采购单
IF 采购单.制单人 # gcUserName OR !EMPTY(采购单.审核)
lErr = .t.
ENDIF
有时候,通过将多个关系表达式用 AND 连接在一起,可以代替嵌套的 IF 语句或DO CASE 语句。例如:
IF THISFORM.cmgEdit.Value = 3 && 单击命令按钮组中的“删除”
IF MESSAGEBOX('是否删除当前记录?',36+256,'提示') = 6 && 确认删除
DELETE
THISFORM.Refresh
ENDIF
ENDIF
可以简化为以下语句:
IF THISFORM.cmgEdit.Value = 3 AND MESSAGEBOX('是否删除当前记录?',36+256,'提示') = 6
DELETE
THISFORM.Refresh
ENDIF
循环结构包括“FOR...”、“DO WHILE...”、“FOR EACH...”和“SCAN”四种结构。
对于“FOR i = 1 to n”这样的循环,需要注意的是:若变量的终止值是一个变量 n,则不要试图在循环内通过修改 n 的值,来达到改变循环次数的目的。这样做的结果是:n 的值确实改变了,但循环次数未变。
我们本来试图通过在循环体内将 n 改为 1 使其仅循环一次,但实际情况却是循环仍然执行了三次。这是因为在执行循环前,系统就将变量转换成了常量,从而确定了循环次数。
若将上例改为“DO WHILE”结构,则情况大为不同:
运行结果表明,循环仅执行了一次。与 FOR 循环不同,DO WHILE 每执行一次循环,系统都要重新计算一次表达式的值,包括其中的变量。因此在循环体内更改变量值对循环次数有影响。
再看一个实例,将上述代码中的变量 n 改为宏替换:
运行结果表明,循环又执行了三次。因为在循环开始时,系统通过宏替换将命令转换成了“DO WHILE i <= 3”,在循环体内改变变量的值对循环次数无影响。
再看下面一个例子,将上述代码修改如下:
运行结果表明,循环仅执行了一次。因为循环开始时,宏替换将命令转换成了“DO WHILE i <= n”。
上述几个例子虽然简单,但其中的细微差别却导致了循环行为有很大的不同,需要用户细细品味,举一反三。
FOR EACH 循环主要针对数组和集合。示例:为表单添加一个自定义方法 SetEnabled,用于启用或禁用表单上的文本框。若文本框无需更改,请在设计时将其 TabStop属性设为 .f.
SCAN 循环仅针对表,注意以下3点:
1. 默认范围是 ALL
2. 不用在循环体内 SKIP
3. 在执行 ENDSCAN 时系统会自动返回 SCAN 时的工作区,无需添加“SELECT 工作区”
最后,举一个应用实例,从中再体会一下 FOR 循环与 DO WHILE 循环的区别。
功能描述:下图中的 List1和 List2均支持多项选择,单击“>”按钮,将左列表中的选定项添加到右列表的尾部,并且选定的列表项从左列表清除。
在“>”按钮的 Click 事件中,若采用 FOR 循环,需使用两个循环,如下所示:
程序代码:
LOCAL oList1,oList2,i oList1 = THISFORM.List1 oList2 = THISFORM.List2 * 将 List1 中选定的列表项添加到 List2 FOR i = 1 TO oList1.ListCount IF oList1.Selected[i] oList2.AddItem(oList1.List[i]) ENDIF NEXT * 将 List1 中选定的列表项删除 FOR i = oList1.ListCount TO 1 STEP -1 IF oList1.Selected[i] oList1.RemoveItem(i) ENDIF NEXT
若采用 DO WHILE,仅使用一个循环即可,如下所示:
程序代码:
LOCAL oList1,oList2,i oList1 = THISFORM.List1 oList2 = THISFORM.List2 i = 1 * 遍历所有列表项 DO WHILE i <= oList1.ListCount IF oList1.Selected[i] && 若列表项被选定 oList2.AddItem(oList1.List[i]) && 添加列表项到 List2 oList1.RemoveItem(i) && 从 List1 中删除列表项 * 删除 List[i] 后,List[i+1] 会变成 List[i] ELSE * 移到下一个列表项 i = i + 1 ENDIF ENDDO
[ 本帖最后由 liuxingang28 于 2014-3-17 11:09 编辑 ]