How do I return a list in Prolog?
Let's say I have these facts:
parent(bob, sam). %bob is sam's parent
parent(sara, sam). %sara is sam's parent
I wanted to find out who were sam's parents and return them开发者_如何学编程 in a list and as such used:
list_parents(P, L) :- findall(Parent, parent(Parent, P), L).
What I want to do now is ask the same question but with only one argument as such:
findParents(sam).
I tried:
findParents(Name) :- list_parents(Name, L).
but this way Prolog simply answers "True"
.
The thing with prolog is that it's a little different than most languages (understatement, if there every was one):
All variables are locally scoped.
Variable values are invariant once bound (unified), unless backtracking unbinds it.
Predicates do not return a value in the conventional sense. They either succeed or fail.
To get value back from testing a predicate, you evaluate the predicate passing it something from your predicate. It doesn't matter if you pass it a variable or a bound value: the called predicate will succeed or value if what the caller has unifies with what you passed it. If you passed a variable, and the called predicate unifies it with a non-variable value, your variable is bound to that value. Think of it (somewhat) as if you had a procedural language where every function returned bool and all parameter values were passed by reference.
What you tried worked:
findParents(Name) :- list_parents(Name, L).
The variable L was unified with (was bound to) the list returned by findall/3
. And then it went out of scope.
If you want to actually do something with that returned (bound) value, you need to deal with it where it's in-scope, or unify that value with something that that predicate was invoked with and thus pass it up the call stack. Or, you could assert it into the database of facts and save it for later.
The way prolog works is that the root predicate used to start your "program" defines a search tree in terms of the predicates in your database. Prolog's "engine" then performs a depth-first, left-to-right search of that tree. Your predicate succeeds when the engine gets to a leaf node of the search tree you defined. Backtracking into your predicate causes the engine to look for the next solution in the search tree.
As a result, anything you want to accomplish in a persistent way has to occur as a side effect of the prolog "engine" evaluating a predicate. For instance print()
always succeeds just once (when you enter the box)...and as a side effect prints whatever you asked it to print. Backtracking into the print doesn't "undo" the print, but print()
doesn't succeed again.
The func library provides a syntax for functions with return values in SWI-Prolog. In this example, you can print all parents of sam
by writing writeln(list_parents $ sam)
:
:- initialization(main).
:- use_module(library(func)).
main :- writeln(list_parents $ sam).
list_parents(P, L) :- findall(Parent, parent(Parent, P), L).
parent(bob, sam). %bob is sam's parent
parent(sara, sam). %sara is sam's parent
Similarly, you can define a function with multiple parameters like this one:
% return a item at an index in a list.
nth0((Index,List),ToReturn) :-
nth0(Index,List,ToReturn).
...and then use it like this:
example :-
ListIndex = (nth0 $(0,[1,2,3,4])), %returns 1, which is the first item in this list
writeln(ListIndex).
you could print the list if want only the user to see it something like:
findParents(Name):-
list_parents(Name,L),
print(L).
but this isnt exactly returning. remember, in prolog, there are no functions and therefore no "return values". you can simulate a function by writing foo(Args,Return) but you can always call it like foo(X,sam) -sometimes it will give what you want, sometimes it wont, sometimes it will crash.
精彩评论