基本上,我尝试创建标准成员谓词,以避免将模块加载到我的项目中。
这是我的代码到目前为止,但不幸的是它不起作用。我究竟做错了什么?我甚至把剪切算子(!)放在那里以确保它有效,但它没有......
/**
* Checks if an element is part of a list
* @param [H|T] List to evaluate
* @param Elem Elem to check
*/
memberCheckSimple([], _):- !, fail. /* stop condition */
memberCheckSimple([H|T], Elem):-
Elem \= H, /* check if element equals head of list and negate it */
memberCheckSimple(T, Elem). /* loop */
memberCheckSimple(_, _). /* only gets here if Elem belongs to list */
答案 0 :(得分:3)
整体谓词逻辑的一个主要问题是它基于故障,并且您试图使成功成为无故障的默认值。这通常与你想要的逻辑相反。你希望它成功,也就是说,你想要建立描述真实情况的事实和规则。
您可以按照以下方式播放。您知道如果元素位于列表的头部,那么它就是列表的元素。所以以下是真的:
memberCheckSimple([H|T], H). /* H is a member of [H|T] */
如果元素是列表尾部的成员,那么元素也是列表的成员:
memberCheckSimple([_|T], H) :- memberCheckSimple(T, H).
这两条规则实际上就是你所需要的。与上述规则之一不匹配的查询将失败,这就是您想要的。
现在看看为什么你的基于故障的谓词不能正常工作并且在失败的情况下取得成功,这是因为这个规则:
memberCheckSimple(_, _).
这表明任何东西都是任何东西的成员。你必须承认,这似乎不合逻辑(因为它不是)。考虑你的先前条款:
memberCheckSimple([], _) :- !, fail.
如果第一个参数是空列表([]
),这可以防止回溯到“普遍真实”子句,但如果非空,则不会。例如,memberCheckSimple([a], b)
最终将通过与第二个子句匹配的路径失败,然后匹配第一个子句。但是第一个子句中的切入并不妨碍memberCheckSimple([a], b)
对第三个子句的回溯(和成功)。你可以通过trace
来观察这一点。
要完成基于故障的方法(我将再次强调,这是问题的错误方法并且存在其他问题,例如不是关系问题),您还需要在第二个条款中进行切割:
memberCheckSimple([H|T], Elem) :-
Elem \= H, /* check if element equals head of list and negate it */
!,
memberCheckSimple(T, Elem). /* loop */
您在代码中的评论表明了一个必要的思考过程。例如,你所谓的“循环”实际上是一种“递归”。另外,正如另一条评论中所提到的,参数排序更自然地表示为列表后面的元素,因为您将其命名为“member”而不是“contains”。