既然你说你对 Prolog 很陌生,我认为如果我们首先评估你的代码而不是立即提供一个工作示例可能会很有用:
delete_all(X,[H|T],Ans):-
X \= H, Ans = H ;
delete_all(X,T,Ans).
一句话,就是这样说的:
-
X不等于H,如果是,将Ans与H统一
或
对尾部递归地做同样的事情
这解释了为什么当元素不等于 X 时您的程序会为您提供单独的结果,并在列表中遇到该元素时返回 false。为了进一步解释为什么你在后一种情况下会出错,让我们看看两个执行的小例子:
delete_all(e,[a,e],Ans)
CASE e \= a -> Ans = e (Prolog returns you Ans)
delete_all(a,[a],Ans)
CASE a = a -> the OR operator triggers and recursion starts
但是糟糕,我们现在进入这个场景:
delete_all(a,[],Ans)
此时,Prolog 无法匹配您的任何代码,因为该列表是空的并且您没有在任何地方指定它。你能看到并理解正在发生的事情(以及出错的地方)吗?
现在我们已经评估了您的代码,让我们尝试编写一个模型,我们可以在其中表达您想要完成的任务。将任何问题分解成更小的部分总是一个好主意,所以让我们开始吧:
我们将从基本情况开始:
% Base case for empty list (_ = wildcard, since we do not care about
% which element we want to delete out of an empty list)
delete_all(_,[],[]).
现在对于我们确实需要处理元素的情况:
% X is not equal to H, so we want to preserve H in our result list
delete_all(X,[H|T],[H|Ans]) :-
...
...
% X is equal to H, so we want to skip H and not add it to our result list
delete_all(X,[H|T],Ans) :-
...
...
如您所见,我们现在为您的问题提供了一个非常易于阅读和结构化的模型。现在剩下的就是编写实际条件和递归调用。我认为您自己尝试编写它们可能是个好主意,因为这是最好的学习方式!
另外,不要抱歉,每个人都必须从某个地方开始,这就是为什么每个人都在这里提供帮助的原因。让我知道你是否成功了,如果没有,我会编辑这篇文章以指定更多信息。
祝你好运!
编辑
你现在已经很接近了,这只是解释的最后一点。考虑以下几点:
% Prepend H to our current result being built recursively
recursion([H|T],[H|Result]):-
recursion(T,Result). % Call recursively with the result tail
所以它的作用是在我们的结果前面添加每个 H,然后使用我们的结果尾部进行实际的递归调用。这就像倒退。
让我们分解一下:
recursion([a,b,c],Result)
% Execution:
(1) case H = a : prepend a to result and call recursively with result tail
(2) case H = b : prepend b to result and call recursively with result tail
(3) case H = c : prepend c to result and call recursively with result tail
现在我们的输入列表变为空,这就解释了为什么我们需要我们的基本情况:
recursion([],[]).
这个案例说:每当我们的输入列表为空时,我们将预先添加到一个空列表以获得我们想要的结果。
如果您想知道为什么是一个空列表,那么 Prolog 就是这样工作的:
[a,b,c] is equivalent to [a,b,c|[]]
您可以在this 和this 问题上阅读更多相关信息。
所以总结一下(仍然没有明确地给你答案:)),你正确地写了条件,基本情况也存在,问题在于你如何进行递归调用。你能弄清楚吗?随时通知我!
编辑
您的尝试和可行的解决方案之间的区别在于您正在进行的递归调用。如果你这样做:
recursion([H|T],R) :-
recursion(T,[H|R])
你最终会得到一个反向列表,因为你在 H 到 R 之前进行了递归调用,这不是我们在这种情况下想要的。这就是我们在 delete_all 的头部指定前置的原因,并且 - 这就是您的代码无法正常工作的原因 - 仅使用结果递归调用。这也是我们用空列表编写基本情况的原因。我给你举个简单的例子:
% We prepend H to our result in the recursive call
recursion([],R) :- write('Ended with result: '), writeln(R).
recursion([H|T],R) :-
recursion(T,[H|R]). % <- here
输出:
?- recursion([1,2,3],R).
Ended with result:
[3, 2, 1|_G5835] % <- note the uninstantiated var and reversed order
正确示例:
% We prepend H to our result in the head of recursion2
recursion2([],[]).
recursion2([H|T],[H|R]) :- % <- here
recursion2(T,R). % and recursive call with just R
这个输出:
?- recursion2([1,2,3],R).
R = [1, 2, 3]
因此,您只需要编辑递归调用,而不是在结果中添加 H,而是在脑海中进行操作,然后在递归调用中传递您的 Ans var。
% We prepend H to Ans in the head of delete_all
delete_all(X,[H|T],[H|Ans]) :-
...
delete_all(X,T,Ans). % <- and make the recursive call with just Ans
这应该可以解决它:)。