【补充篇】PBIDEA 全局函数 choice:从多个值里挑一个(13 条边界实测 + 两个避坑)
本篇是上一楼《PowerBuilder 控制结构全解:IF / CHOOSE CASE / FOR / DO LOOP》的补充篇 —— 主题独立,讲的是 PBIDEA 新增的全局函数 choice。
前置标注
- 适用版本:PBIDEA(PbIdea.dll)提供,以 SYSTEM library 外部函数形式声明;本文实机版本 PB12.5
- 难度系数:★★☆☆☆(2 / 5)
- 数据库:不依赖数据库
- 运行环境:运行目录内必须有 PbIdea.dll(本文实测用 D:\PBAI\UPDATE\125\PbIdea.dll,放进 run 目录后 5 个重载全部可调用)
- 操作系统:Windows
一、它是怎么声明的:5 个重载指向同一个 DLL 导出
- global function any choice(boolean bCheck, readonly any a, readonly any b) SYSTEM library "PbIdea.dll" alias for "fnChoice"
- global function any choice(int index, ...) SYSTEM library "PbIdea.dll" alias for "fnChoice"
- global function any choice(readonly any arr[]) SYSTEM library "PbIdea.dll" alias for "fnChoice"
- global function string choice(readonly string str) SYSTEM library "PbIdea.dll" alias for "fnChoice"
- global function long choice(long maxValue) SYSTEM library "PbIdea.dll" alias for "fnChoice"
复制代码
以上 5 行取自 PBIDEA 源码 choice.srf,原文照抄未做改动。注意第 2 行用了 ... 变参:自定义 PowerScript 函数不支持变参,但外部函数声明可以 —— 所以 choice 才能按序号取"不定个数"的候选。
二、五个重载分别做什么(PB12.5 实机结果)
1. 二选一 choice(boolean bCheck, any a, any b):bCheck 为 true 返回 a,否则返回 b。实测 b_true=a、b_false=b。
2. 按序号取 choice(int index, ...):返回第 index 个后续实参,index 是 1 基。实测 i_1=a、i_2=b、i_3=c;只有一个候选时 choice(1, a) = a。
3. 数组随机 choice(any arr[]):从数组里随机取一个元素。实测 3 元素数组跑 20 次,x / y / z 都出现过。
4. 字符随机 choice(string str):从字符串里随机取 1 个字符。实测 20 次 choice('abcdef') 得到 20 个字符,样本 afcdcebebeedadcabfbc。
5. 整数随机 choice(long maxValue):返回 [0, maxValue) 内的整数。实测 200 次 choice(10),最小 0、最大 9。
返回值类型:候选是字符串时 ClassName(any) 得到 string —— any 里装的就是候选本身,没有做类型转换。
三、第一个坑:choice 从不抛异常,非法输入一律"静默返回 NULL"
13 条边界输入逐条隔离实测,全部 BRANCH=OK —— 一条异常都没抛:
○ choice(0, a, b, c) 索引 0,低于下界 → NULL
○ choice(5, a, b, c) 索引越过候选个数 → NULL
○ choice(2, a) 只有 1 个候选却要第 2 个 → NULL
○ choice(-1, a, b, c) 负索引 → NULL
○ choice(0) maxValue = 0 → NULL
○ choice(-5) maxValue 为负 → NULL
○ choice('') 空串 → NULL
○ choice(true, NULL串, 'a') 候选本身是 NULL → NULL(原样把那个 NULL 返回)
○ 对照组全部正常:choice(2, a, b, c)=b、choice(1)=0、choice('abcdef')=随机字符、choice(false, a, b)=b
结论:别指望 try ... catch (runtimeerror) 兜住 choice 的错误用法 —— 它不报错,直接给你 NULL。必须自己 IsNull() 判空。
四、第二个坑:一个 NULL 会污染整条拼接串
这个坑我在测试里连踩两次,值得单独说。原写法:
- ls_ret = 'E1=' + String(la_r) + ';'
复制代码
当 la_r 是 NULL(choice 越界正是这种),String(la_r) 也是 NULL,而 PB 的 + 拼接遇到 NULL 就得 NULL —— 于是 ls_ret 整条变成 NULL,后面所有追加全部失效,最后 FileWrite 出去是一个 0 字节的空文件。
排查信号很有辨识度:函数明明正常返回、测试也报 PASS,但诊断文件是 0 字节,十有八九就是 NULL 传染。本次就是靠"9 条用例的 pre / after 文件全都生成了、after 却是 0 字节"这一现象倒推出根因的。
正确写法 —— 每个参与拼接的值先判空:
- string ls_ret, ls_v, ls_a, ls_b
- any la_r
- ls_a = 'a'
- ls_b = 'b'
- la_r = choice(5, ls_a, ls_b) // 索引越界,返回 NULL
- if IsNull(la_r) then
- ls_v = '<NULL>'
- else
- ls_v = String(la_r)
- end if
- ls_ret = 'r=' + ls_v
复制代码
五、版本陷阱:fnChoice 不是每个 PbIdea.dll 都有
解析 PE 导出表(只读)实测:
● D:\PBAI\UPDATE\10\PbIdea.dll —— 18,022,400 字节 / 2545 个导出 / 有 fnChoice
● D:\PBAI\UPDATE\125\PbIdea.dll —— 17,978,368 字节 / 2545 个导出 / 有 fnChoice
● D:\PBAI\PbIdea.dll —— 17,893,888 字节 / 2520 个导出 / 没有
● pblit_test\PbIdea.dll —— 17,892,864 字节 / 2520 个导出 / 没有
● UPDATE\125\runtest\PbIdea.dll —— 15,122,944 字节 / 2201 个导出 / 没有
注意:体积大不等于版本新(125 的 17,978,368 反而比 10 的 18,022,400 小一点),而且同目录下的 runtest 是个覆盖面小得多的精简运行时。某个函数到底有没有,只能查导出表 —— 别看文件大小,也别信目录名。
六、典型用法
- // 1) 二选一,替代 if / else 赋值
- ls_mode = choice(ib_debug, '调试模式', '生产模式')
- // 2) 按用户选择取第 n 个候选(n 从 1 开始)
- ls_col = choice(li_col, 'id', 'name', 'dept')
- // 3) 随机抽样
- ls_pick = choice(ls_names) // 数组里随机取一个
- ls_ch = choice('abcdef0123456789') // 随机取 1 个字符
- li_roll = choice(6) + 1 // 掷骰子:1 ~ 6
复制代码
七、边界与注意事项汇总
● index 是 1 基;0 / 负数 / 越界全部 → NULL,且不报错
● maxValue ≤ 0 → NULL(choice(0)、choice(-5) 实测都是 NULL)
● maxValue = 1 → 恒为 0
● 空串 → NULL,不是空串!所以判空只能用 IsNull(),用 Len(s) = 0 判断会漏
● 候选值本身是 NULL → 原样返回 NULL
● 本文只验证了"抽得到",没有做均匀性统计检验,别把它当均匀分布使用
● 属外部函数,运行目录必须有 PbIdea.dll
● 不要用在安全场景(令牌 / 密码 / 验证码)—— 随机源性质未公开
八、验证方式
- 测试工程:choice.srf(PBIDEA 原源码)+ nvo_choice_demo(正常路径)+ nvo_choice_edge(13 条边界逐条隔离)
- 实机四步全绿:import 0 错 + rebuild --type full 0 错 + PBVM 真机运行 PASS + 源码自检 0 错 0 提醒 / 编码检查 bad = 0
- 诊断产物:diag_choice_safe.txt(正常路径全量输出)、diag_c3_*.txt(边界逐条 pre / after)
- 未实测 · 仅静态核对:抽样的均匀性、多会话下的随机源行为 —— 本文未做,随机性结论仅限"能抽到、分布未检验" |