【问题标题】:How to make query result appear as a list [duplicate]如何使查询结果显示为列表[重复]
【发布时间】:2018-07-02 07:11:35
【问题描述】:

我正在尝试制作一个小型 prolog 程序,该程序可以从列表中删除特定数字,但我似乎无法让它生成列表。

代码通过

delete_all(X,[H|T],Ans):- X\=H ,Ans= H; delete_all(X,T,Ans).

当我输入时

| ?- delete_all(x,[a,b,x],Answer).

我明白了

Answer = a ? ;
Answer = b ? ;
no

到目前为止我尝试过

| ?- findall(Ans,delete_all(x,[a,b,x],Ans),Ans).

返回

Ans = [a,b]

是的,我想要这个输出,但我不想改变我的查询形式。 有没有其他方法可以做到这一点?

如果我错过了基础知识,我很抱歉,这真的很新。

【问题讨论】:

    标签: prolog


    【解决方案1】:

    既然你说你对 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 无法匹配您的任何代码,因为该列表是空的并且您没有在任何地方指定它。你能看到并理解正在发生的事情(以及出错的地方)吗?

    现在我们已经评估了您的代码,让我们尝试编写一个模型,我们可以在其中表达您想要完成的任务。将任何问题分解成更小的部分总是一个好主意,所以让我们开始吧:

    • 我们可以尝试删除空列表中的某些内容 -> 这应该总是正确的

    • 我们可以尝试删除非空列表中的某些内容,但我们希望返回实际生成的 List,而不是删除的元素。为此,我们需要在整个递归过程中跟踪每个不需要删除的元素,并跳过我们想要删除的元素。

    我们将从基本情况开始:

    % 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|[]]
    

    您可以在thisthis 问题上阅读更多相关信息。

    所以总结一下(仍然没有明确地给你答案:)),你正确地写了条件,基本情况也存在,问题在于你如何进行递归调用。你能弄清楚吗?随时通知我!

    编辑

    您的尝试和可行的解决方案之间的区别在于您正在进行的递归调用。如果你这样做:

    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
    

    这应该可以解决它:)。

    【讨论】:

    • this 错了吗?我不断得到一个“不”
    • 我编辑了我的答案,试图解释为什么你的代码还不能工作。让我知道你是否能正常工作。
    • 是终止的问题吗?我一直在重读,但不明白我应该改变什么?你说在答案末尾添加一个空列表,但我不知道该怎么做......
    • 查看以下编辑。我只是注意到它已经变得很长了,对不起,我让自己去那里有点太多了:)。
    • 谢谢!我现在明白了。我衷心感谢您的帮助。我希望你有个美好的一天。附带问题:我仍然不明白为什么在头部添加不会产生反向列表。
    猜你喜欢
    • 2018-05-11
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 2015-12-18
    • 2018-01-18
    相关资源
    最近更新 更多