Prolog编写支持多新元素的sn列表更新谓词update的实现问题
问题描述
我们需要实现Prolog谓词update(NewElements, OldList, NewList),其中所有条目格式为sn(N, C, P),处理规则为:
- 遍历OldList中的每一个
sn(N, _, _)条目 - 如果NewElements中存在相同N值的条目,且该新条目的C值小于旧条目的C值,就用新条目替换旧条目
- 最终生成的替换后列表即为NewList
示例查询:
update([sn(b,5,b), sn(a,1,a)], [sn(a,2,a), sn(b,4,b)], [sn(a,1,a), sn(b,4,b)]).
运行结果为true。
现有代码及缺陷
目前编写的代码如下:
same(X, X). update_helper(_, [], []). update_helper(sn(N1, C1, P1), [sn(N2, C2, P2)|T1], [sn(N3, C3, P3)|T2]) :- \+ N1 == N2, same(sn(N2, C2, P2), sn(N3, C3, P3)), update_helper(sn(N1, C1, P1), T1, T2). update_helper(sn(N1, C1, P1), [sn(N2, C2, P2)|T1], [sn(N3, C3, P3)|T2]) :- N1 == N2, C1 < C2, same(sn(N1, C1, P1), sn(N3, C3, P3)), update_helper(sn(N1, C1, P1), T1, T2). update_helper(sn(N1, C1, P1), [sn(N2, C2, P2)|T1], [sn(N3, C3, P3)|T2]) :- N1 == N2, C1 >= C2, same(sn(N2, C2, P2), sn(N3, C3, P3)), update_helper(sn(N1, C1, P1), T1, T2). update([], _, _). update([H|T], Old, New) :- update(T, Old, New), update_helper(H, Old, New).
该代码仅在NewElements仅含单个元素时可正常运行,核心缺陷有3点:
- 递归逻辑未传递中间处理结果:每次调用
update_helper时,传入的都是原始OldList,而非上一次更新后的列表,多个更新规则无法叠加生效 - 递归执行顺序颠倒:先递归处理NewElements的剩余尾部,再处理当前头元素,完全不符合“逐个处理更新、逐步生成结果”的逻辑
- 空列表子句无校验:
update([], _, _)只要更新列表为空就直接成功,不管Old和New是否匹配,容易产生无意义的错误结果
修正实现
有两种可行的实现思路,推荐第一种更简洁直观:
思路1:遍历旧列表匹配更新规则
不需要逐个拿新元素扫描旧列表,反过来遍历每个旧条目,去NewElements里找符合条件的替换项即可:
% 辅助谓词:匹配单个旧条目是否需要更新 match_update(sn(N_old, C_old, _), NewElements, OldSn, NewSn) :- % 在NewElements中找同N且C更小的条目 ( member(sn(N_new, C_new, P_new), NewElements), N_new == N_old, C_new < C_old -> NewSn = sn(N_new, C_new, P_new) % 符合条件则替换 ; NewSn = OldSn % 不符合则保留原条目 ). % 主谓词:递归处理整个旧列表 update(_, [], []). update(NewElements, [OldHead|OldTail], [NewHead|NewTail]) :- match_update(OldHead, NewElements, OldHead, NewHead), update(NewElements, OldTail, NewTail).
思路2:修正原有递归逻辑
如果要保留“逐个处理NewElements元素”的思路,只需要修正update谓词的递归逻辑,传递中间处理结果即可:
% 保留原有update_helper逻辑不变 same(X, X). update_helper(_, [], []). update_helper(sn(N1, C1, P1), [sn(N2, C2, P2)|T1], [sn(N3, C3, P3)|T2]) :- \+ N1 == N2, same(sn(N2, C2, P2), sn(N3, C3, P3)), update_helper(sn(N1, C1, P1), T1, T2). update_helper(sn(N1, C1, P1), [sn(N2, C2, P2)|T1], [sn(N3, C3, P3)|T2]) :- N1 == N2, C1 < C2, same(sn(N1, C1, P1), sn(N3, C3, P3)), update_helper(sn(N1, C1, P1), T1, T2). update_helper(sn(N1, C1, P1), [sn(N2, C2, P2)|T1], [sn(N3, C3, P3)|T2]) :- N1 == N2, C1 >= C2, same(sn(N2, C2, P2), sn(N3, C3, P3)), update_helper(sn(N1, C1, P1), T1, T2). % 修正后的update递归逻辑 update([], Old, Old). % 空更新列表时,最终结果就是处理完的Old列表 update([H|T], Old, New) :- update_helper(H, Old, Mid), % 用当前H处理Old,得到中间结果Mid update(T, Mid, New). % 用Mid作为输入,处理剩余的更新元素
内容的提问来源于stack exchange,提问作者Heide
相关产品推荐
相关产品推荐

