“方案”与程序自动生成
明白了反向推理是怎么回事,自动生成程序也就不难了。
把“方案”加入反向推理规则
用到的办法,是把 Python 的函数弄成反向推理规则。这些函数是在 .krb 文件中,每个规则结尾处的 with 子句处。它们不影响规则对目标的证明,而是一起形成“流程图”,返回相应的模式变量,其中约束的值,是证明最终目标的证据。
示例
有个小小的规则库,用于实现银行帐户之间的转帐。转出、转入帐户采取以下形式之一:
- (户名, 帐户类型)
- 这是本行帐户。
- 例如:('bruce', 'checking')。
- (银行名称,户名, 帐户类型)
- 这是其他银行的帐户。
- 例如:('my_other_bank', 'bruce', 'checking').
其中至少有一个帐户,必须是本银行的。
下面是规则库的示例:
1 transfer1 2 use transfer($from_acct, $to_acct) taking (amount) 3 when 4 withdraw($from_acct) 5 $$(amount) 6 deposit($to_acct) 7 $$(amount) 8 transfer2 9 use transfer($from_acct, $to_acct) taking (amount) 10 when 11 transfer_ach($from_acct, $to_acct) 12 $$(amount) 13 withdraw 14 use withdraw(($who, $acct_type)) taking (amount) 15 with 16 print "withdraw", amount, "from", $who, $acct_type 17 deposit 18 use deposit(($who, $acct_type)) taking (amount) 19 with 20 print "deposit", amount, "to", $who, $acct_type 21 transfer_ach1 22 use transfer_ach($from_acct, ($bank, $who, $acct_type)) taking (amount) 23 when 24 withdraw($from_acct) 25 $$(amount) 26 deposit((central_accts, ach_send_acct)) 27 $$(amount) 28 with 29 print "send", amount, "to bank", $bank, "acct", $who, $acct_type 30 transfer_ach2 31 use transfer_ach($from_acct, $to_acct) taking (amount) 32 when 33 get_ach($from_acct) 34 $$(amount) 35 withdraw((central_accts, ach_recv_acct)) 36 $$(amount) 37 deposit($to_acct) 38 $$(amount) 39 get_ach 40 use get_ach(($bank, $who, $acct_type)) taking (amount) 41 with 42 print "get", amount, "from bank", $bank, "acct", $who, $acct_type
示例中方案函数的产生
其中每一规则,都会产生一个“方案函数”。方案函数的名称与对应规则的名称相同,例如,第一个规则产生的方案函数,叫做“transfer1”。
示例中,第一个规则产生的方案函数,内容包括第5行和第7行。事实withdraw($from_acct)和deposit($to_acct)证明为真(匹配)后,会分别产生方案函数。按照第2行taking子句的规定,函数会有个“amount”参数。
示例第13行规则“withdraw”产生的方案函数,只有在 with 子句里的第 16 行语句。其中模式变量 $who 和 $acct_type,它们的值会在终极目标证明后,得到相应的约束(赋值)。
第21行规则产生的方案函数“transfer_ach1”,内容有3行,其中2行在 when 子句里,另外一句是在 with 子句的第 29 行。从语法上看,这些语句在同一级别的块块里,但在 .krb 文件中不是这样。
运行示例
“方案”是目标证明过程中产生的副产品:
>>> from pyke import knowledge_engine
>>> engine = knowledge_engine.engine(__file__) >>> engine.activate('plan_example') >>> no_vars, plan1 = / ... engine.prove_1_goal( ... 'plan_example.transfer((bruce, checking), (bruce, savings))')
现在,plan1 成了转账程序,用以把 bruce 帐户里的支票,转成存款。plan1 也从规则名,变成了函数名:
Plan1
可以像调用标准函数那样,用参数向定义在 taking 子句中的变量传值。taking 子句,处于终极目标规则(transfer)里。
>>> plan1(100) withdraw 100 from bruce checking deposit 100 to bruce savings
这段程序可以多次调用:
>>> plan1(50) withdraw 50 from bruce checking deposit 50 to bruce savings
注意输出的字符串:obruce, checking 和 savings。它们是模式变量,经过调制置入函数调用图的方案中。
再来写第二个程序:
>>> no_vars, plan2 = / ... engine.prove_1_goal( ... 'plan_example.transfer((bruce, checking), ' ... '(my_other_bank, bruce, savings))')
现在,plan2 这个程序,用以把 bruce 在其他银行的支票,转为本银行的存款:
Plan2
它的运行与 plan1 相同,但产生的结果不一样:
>>> plan2(200) withdraw 200 from bruce checking deposit 200 to central_accts ach_send_acct send 200 to bank my_other_bank acct bruce savings
最后一个用例:
>>> no_vars, plan3 = / ... engine.prove_1_goal( ... 'plan_example.transfer((my_other_bank, bruce, checking), ' ... '(bruce, savings))') >>> plan3(150) get 150 from bank my_other_bank acct bruce checking withdraw 150 from central_accts ach_recv_acct deposit 150 to bruce savings
程序 paln3 是这样的:
Plan3
注意,在程序 plan2 和 plan3 里,函数 transfer2 调用两个不同的函数 transfer_ach1 和 transfer_ach2。这说明根据推理规则不同,可以选择不同函数。还有,在 plan3 生成后,plan2 仍可运行使用;它俩都能调用成功,并根据函数 transfer2 设置的初始值,产生不同结果。
结论
可见,把 Pyke 和 Python 函数自动整合到程序中,非常容易!
允许在 Python 函数中,用模式变量设置数据,供 Pyke 匹配操作时使用。