第一次编写 Prolog 可能会让人望而生畏,因为它与您很可能会遇到的许多传统编程语言不同;但是,一旦您掌握了这种新的编程风格,这将是一次非常有益的体验!既然您提到您是第一次编写 Prolog,我将提供一些有关编写 Prolog 的一般提示和技巧,然后针对您的问题提出一些提示,然后提供我认为的内容一个解决方案。
递归思考
您可以认为您编写的每个 Prolog 程序本质上都是递归的。即,您可以为其提供一系列“基本案例”,其形式如下:
human(John). 或 wildling(Ygritte) 在我看来,这些规则应该始终是您编写的第一条规则。尝试将问题分解为最简单的情况,然后从那里开始工作。
另一方面,您还可以为其提供更复杂的规则,如下所示:contains(X, [H|T]):- contains(X, T) 关键是编写这样的规则非常等效于在 Python 中编写递归函数。这条规则在查看一个值是否包含在列表中时做了很多繁重的工作,但如果没有“基本情况”,它是不完整的。一个完整的包含规则实际上是 两个 规则放在一起:
contains(X, [X|_]).
contains(X, [H|T]):-contains(X, T).
最大的收获是尝试确定问题的简单案例,这些案例可以像递归函数中的基本案例一样,然后尝试确定您希望如何“递归”并实际解决手头的问题。
模式匹配
Prolog 的部分优点在于它拥有的模式匹配系统。你应该 100% 尽可能地利用这一点——在尝试对列表做任何事情时它特别有用。例如:
head(X, [X|T]).
如此调用时将评估为真:head(1, [1, 2, 3]) 因为规则中的内在是 X 的匹配。这种对列表第一个元素的模式匹配非常重要,并且确实是您对列表进行任何工作的关键方式在序言中。根据我的经验,列表头部的模式匹配通常是我之前提到的“基本情况”之一。
了解程序的流程
Prolog 工作方式的另一个关键组成部分是它采用“自上而下”的方法来阅读代码。我的意思是每次调用规则时(king(James). 形式的定义除外),Prolog 从第 1 行开始,一直持续到到达为真规则或文件末尾。因此,规则的顺序非常重要。我假设您知道您可以通过逗号将规则组合在一起以表示逻辑 AND,但更微妙的是,如果您将一个规则置于另一个之上,它可以充当逻辑 OR,仅仅是因为它将是在另一个规则之前评估,并可能导致程序递归。
具体例子
现在我已经了解了我所有的一般建议,我将实际参考给定的问题。首先,我会写我的“基本案例”。如果给你两个第一个元素相同的列表,会发生什么?
如果每个列表中的第一个元素不相同,则它们必须不同。因此,您必须查看第二个列表以查看该元素是否包含在列表其余部分的任何位置。这会产生什么样的规则?
或者,第一个列表的第一个元素可能根本不包含在第二个列表中,在这种情况下,您必须在第一个列表中前进一次,然后从第二个列表重新开始。 this 会产生什么样的规则?
最后,我想说你的方法是正确的,我在下面提供了我自己的解决方案:
similarity([H|_], [H|_]).
similarity(H1|T1], [_|T2]):- similarity([H1|T1], T2).
similarity([_|T1], [H2|T2]):- similarity(T1, [H2|T2]).
希望所有这些都能在某种程度上有所帮助!