Get the output value from a Prolog recursive function

和自甴很熟 提交于 2021-02-10 16:56:18

问题


I am learning Prolog these days and need to combine two list into one that follows this rule:

ListA: [], ListB: [] => ResultList: []
ListA: [], ListB:[a,b,c] => ResultList: [[a],[b],[c]]
ListA: [1,2], ListB:[a,b,c] => ResultList: [[1,2,a],[1,2,b],[1,2,c]]

I am stuck with a recursive function output value problem and here is my coding:

extend_my_path([],_,_,_).
extend_my_path([H|T],OriginalPath,OrignailMewPaths,NewPaths) :-
    append(OriginalPath,[H],NewPath),
    append(OrignailMewPaths,[NewPath],NewPaths1),
    print("NewPaths1:"),print(NewPaths1), nl,
    extend_my_path(T,OriginalPath,NewPaths1,NewPaths1).

Running it gives the following output without the value of Result varable:

?- extend_my_path([a,b,c],[1,2],[],Result).
"NewPaths1:"[[1,2,a]]
"NewPaths1:"[[1,2,a],[1,2,b]]
"NewPaths1:"[[1,2,a],[1,2,b],[1,2,c]]
true.

I wish to get its value like:

Result=[[1,2,a],[1,2,b],[1,2,c]]

It is highly appreciated if anyone can point out the reason. Thanks.


回答1:


I see quite a bit of confusion in these six lines of code. Here are some clues that something is amiss:

  • Your problem is defined in terms of three things, but your predicate has four arguments. What are the last two supposed to be?
  • You have a singleton variable NewPaths which is a disaster
  • Your base case relates an empty list to any three other things! Try it: extend_my_path([], lightning, [the,sound,of,rain], "fresh coffee") is true! This seems unlikely to be what you intended.
  • One wonders what an "Orignail Mew Paths" is, or what "paths" have to do with anything here. Typos matter, fix them!
  • The inductive case ends with two copies of NewPaths1, which seems suspicious because we had two very different variables at the start, and as you see in the base case it doesn't matter what they are. Odd!

I think what happened here is that you figured the only and right way to add things to a list is with append/3, which isn't true. I think further Prolog's single-assignment semantics convinced you that you needed another argument to create the empty list for you to append to. You missed the base case for your recursion so you never got a sensible return; making it wide open at least got you to true but didn't give you a binding. And the fundamental problem here is that there isn't a sensible relation in your code. Let's try again.

You're trying to write a predicate whose name and arguments are something more like this:

% extend_path(Path, Possibilities, NewPaths)

There's no need for a fourth argument here, because you don't need to know what you've done so far in creating NewPaths, you can just build the paths for the current possibility and know that recursion is taking care of the rest.

Your base case would then be the case where you have no more possibilities, and thus, no new paths:

extend_path(_, [], []).

This doesn't say anything-relates-to-anything, it says "the path extensions of any prefix, when I'm out of possibilities, is empty."

Your inductive case then takes your first argument and appends it onto the next item from your list of possibilities. This goes on your list of new paths.

extend_path(Prefix, [Ext1|ExtRest], [Next1|NextRest]) :-
    append(Prefix, [Ext1], Next1),
    extend_path(Prefix, ExtRest, NextRest).

This literally says "given some Prefix, and that I have an Ext left in my list of extensions, will produce a new path extension Next1, such that: Prefix + [Ext1] is Next1." Actually, it says a little more than that with the recursive step. But the idea here is bite off one piece, Ext1, and match it up with one piece of the result, Next1, and then match up the rest of the input with the rest of the output.

Lastly, please do not treat a singleton variable "warning" as a warning. This is not like Python being mad that you don't have enough newlines between functions. Singleton variables are almost always CRITICAL ERRORS, especially for new users of Prolog! Pay attention to them!

Hope this helps!




回答2:


Instead of trying to fix your existing code let's try solving the problem afresh. You show the examples:

ListA: [],    ListB: []      => ResultList: []
ListA: [],    ListB: [a,b,c] => ResultList: [[a],[b],[c]]
ListA: [1,2], ListB: [a,b,c] => ResultList: [[1,2,a],[1,2,b],[1,2,c]]

and

?- extend_my_path([a,b,c], [1,2], [], Result).
Result = [[1,2,a],[1,2,b],[1,2,c]]

The third argument looks completely out of place, and the first and the second arguments are swapped compared to the first batch of examples, so let's say it's supposed to be

?- ListA = [1,2], ListB = [a,b,c], extend_my_path( ListA, ListB, ResultList).
ResultList = [[1,2,a],[1,2,b],[1,2,c]]

So we start coding by just listing the cases we know must hold:

extend_my_path_ex( [], [], []).
extend_my_path_ex( [],    [a,b,c], [[    a],[    b],[    c]]).
extend_my_path_ex( [1,2], [a,b,c], [[1,2,a],[1,2,b],[1,2,c]]).

Generalizing it gives us

extend_my_path3( [], [], []). 
extend_my_path3( OneTwo, [A,B,C], [OneTwoA,OneTwoB,OneTwoC]):-
    append( OneTwo, [A], OneTwoA),
    append( OneTwo, [B], OneTwoB),    % ... right? all the examples produce 
    append( OneTwo, [C], OneTwoC).    %     the same answers. check it!

And what if there were only two of them?

extend_my_path2( [], [], []).  
extend_my_path2( OneTwo,   [B,C],         [OneTwoB,OneTwoC]):-
    append( OneTwo, [B], OneTwoB),
    append( OneTwo, [C], OneTwoC).    % ... right? 

So we can actually just re-write it syntactically as

extend_my_path3( [], [], []). 
extend_my_path3( OneTwo, [A | [B,C]], [OneTwoA | [OneTwoB,OneTwoC] ]):-
    append(      OneTwo, [A],          OneTwoA),
    extend_my_path2( OneTwo,  [B,C],             [OneTwoB,OneTwoC]  ).

But look! Whatever we were doing with the three elements, we are doing the same thing, essentially, with the two.

And why should we limit ourselves to the three, or the two elements cases only? There's nothing special about those. Let's generalize once more,

extend_my_path( [], [], []).
extend_my_path( OneTwo, [A | B_C], [OneTwoA | OneTwoB_OneTwoC ]):-
    append(     OneTwo, [A],        OneTwoA),
    extend_my_path( OneTwo,  B_C,             OneTwoB_OneTwoC ).

Can you take it from here?



来源:https://stackoverflow.com/questions/52395790/get-the-output-value-from-a-prolog-recursive-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!