How do I return a list in Prolog?

17,525

Solution 1

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.

Solution 2

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).

Solution 3

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.

Share:
17,525
porlognewbie
Author by

porlognewbie

Updated on June 04, 2022

Comments

  • porlognewbie
    porlognewbie almost 2 years

    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".

  • porlognewbie
    porlognewbie almost 13 years
    That's my problem though. I've programmed in many languages before but I just can't get my head around how this is supposed to work. I don't need it printed though, I need the same result I would get calling the list_parents "function".
  • Thanos Tintinidis
    Thanos Tintinidis almost 13 years
    well, just call the predicate list_parents with the variable where you want to store the list. like list_parents(sam,List). then prolog will unify the List with the list of sam's parents. if it makes it easier you could imagine that it's the equivalent of List=list_parents(sam), but it's not quite true.