error handling in gprolog? - prolog

In order to run a group of queries in a simple query, I have tagged these queries and use forall/2 to call them:
query_all :-
forall(query(Q), (Q ->
format('yes: ~w~n',[Q]) ;
format('no : ~w~n',[Q]))).
so if I define something like query(true)., I'll be able to see yes: true from the output.
The problem here is that query( ... ) do not always exist, when prolog can't find anything that tagged query, forall/2 will fail and cause exception saying "error(existence_error(procedure,query/1),forall/2)"
I want to handle this exception, but not to break the whole control flow.
I know catch/3 would help me but I don't know how to use it, my code is:
catch(query_all, error(existence_error(procedure,_),_), recovery).
recovery :-
format('error occurred.~n',[]).
but prolog says "native code procedure catch/3 cannot be redefined".
Is there something I've missed?

You can either declare query/1 as dynamic in your code adding this line:
:-dynamic(query/1).
or use catch/3 as you suggested, however you don't have to redefine it but use it instead, e.g:
query_all :-
catch(
forall(query(Q), (Q ->
format('yes: ~w~n',[Q]) ;
format('no : ~w~n',[Q]))),
error(existence_error(procedure, _), _), format('error occurred.~n', [])).

Related

Prolog: Redirect system output

There are Prolog predicates that output error messages, like load_files/1 in case the file could not be found:
error(existence_error(source_sink,'/home/foo/nothinghere.txt'),_3724).
These error messages are just printed to stdout but are not returned as prolog return value.
In my case, load_files/1 gets called deep down as part of a procedure that I don't want to modify. I just deliver the file name and wait for the return value. But as far as I understand, the return value, in this case, is either True or an error. Is there any way for me to redirect the error output to the return value so that I can actually do something with the output I am getting?
You could use catch/3.
?- catch(load_files('/tmp/exists.prolog'), E, true).
true.
?- catch(load_files('/tmp/notexists.prolog'), E, true).
E = error(existence_error(source_sink, '/tmp/notexists.prolog'), _).
catch(:Goal, +Catcher, :Recover) is used for catching throw(_) from the :Goal.
Catcher is unified with the argument of throw/1.
Recover is called using call/1.
In the above example E unifies with any error. We do not want that, so we can do something like:
catchFileErrors(G, F) :-
catch(G, error(existence_error(source_sink, F), _), true).
?- catchFileErrors(load_files('exists.prolog'), E).
true.
?- catchFileErrors(load_files('notexists.prolog'), E).
E = 'notexists.prolog'.
You can pass a goal as the last argument to catch if you have a strategy to recover from the error.

Converting database to facts in Prolog

So I have database which looks something like:
DB = [
data([table, keyboard,cup, box,watch]),
data([green,red, yellow,white,blue]),
data([alex, john,sasha, sabrina, ben]),
data([coffee, tea, syrup, vodka, beer]),
data([bookA, bookB, bookC, bookD, bookE])
]
I would like to save DB as a fact. Then we should create a relation db_to_facts which finds all the facts.
Example:
data([true, false]).
data([dog,cat]).
Output:
db_to_facts(DB).
DB = [data([true, false]), data([dog, cat])].
What would be the cleanest way possible to achieve it?
Edit:
I think I got it:
db_to_facts(L) :- findall(data(X),data(X),L).
But if the database is empty, it will fail. How to make it return empty list?
In the beginning of your Prolog program, use the directive, dynamic(data/1).. This tells Prolog you have a dynamic database that can change over time and will still recognize the data(X) query even if there is no data.
Without the directive:
1 ?- data(X).
ERROR: Undefined procedure: data/1 (DWIM could not correct goal)
2 ?-
With the directive:
2 ?- dynamic(data/1).
true.
3 ?- data(X).
false.
And then your findall/3 call will yield [] if there is no data.
For sure, the usage of dynamic(data/1) is the best way. Just to let you know, there is another way to check if data/1 exists. You can use current_predicate/2 in this way:
db_to_facts(L):-
( current_predicate(data,_) -> findall(data(X),data(X),L) ; L = []).
If you compile it (you cannot use swish online, it gives No permission to call sandboxed ...) you get a warning saying that you should define data/1 but if you run the query anyway you get the empty list:
?- db_to_facts(L).
L = [].
It's not the cleanest way but it works :)

Prolog (Sicstus) - nonmember and setof issues

Given following facts:
route(TubeLine, ListOfStations).
route(green, [a,b,c,d,e,f]).
route(blue, [g,b,c,h,i,j]).
...
I am required to find all the pairs of tube Lines that do not have any stations in common, producing the following:
| ?- disjointed_lines(Ls).
Ls = [(yellow,blue),(yellow,green),(yellow,red),(yellow,silver)] ? ;
no
I came up with the below answer, however it does not only give me incorrect answer, but it also does not apply my X^ condition - i.e. it still prints results per member of Stations lists separately:
disjointed_lines(Ls) :-
route(W, Stations1),
route(Z, Stations2),
setof(
(W,Z),X^
(member(X, Stations1),nonmember(X, Stations2)),
Ls).
This is the output that the definition produces:
| ?- disjointed_lines(L).
L = [(green,green)] ? ;
L = [(green,blue)] ? ;
L = [(green,silver)] ? ;
...
I believe that my logic relating to membership is incorrect, however I cannot figure out what is wrong. Can anyone see where am I failing?
I also read Learn Prolog Now chapter 11 on results gathering as suggested here, however it seems that I am still unable to use the ^ operator correctly. Any help would be appreciated!
UPDATE:
As suggested by user CapelliC, I changed the code into the following:
disjointed_lines(Ls) :-
setof(
(W,Z),(Stations1, Stations2)^
((route(W, Stations1),
route(Z, Stations2),notMembers(Stations1,Stations2))),
Ls).
notMembers([],_).
notMembers([H|T],L):- notMembers(T,L), nonmember(H,L).
The following, however, gives me duplicates of (X,Y) and (Y,X), but the next step will be to remove those in a separate rule. Thank you for the help!
I think you should put route/2 calls inside setof' goal, and express disjointness more clearly, so you can test it separately. About the ^ operator, it requests a variable to be universally quantified in goal scope. Maybe a concise explanation like that found at bagof/3 manual page will help...
disjointed_lines(Ls) :-
setof((W,Z), Stations1^Stations2^(
route(W, Stations1),
route(Z, Stations2),
disjoint(Stations1, Stations2)
), Ls).
disjoint(Stations1, Stations2) :-
... % could be easy as intersection(Stations1, Stations2, [])
% or something more efficient: early fail at first shared 'station'
setof/3 is easier to use if you create an auxiliary predicate that expresses the relationship you are interested in:
disjoint_routes(W, Z) :-
route(W, Stations1),
route(Z, Stations2),
disjoint(Stations1, Stations2).
With this, the definition of disjointed_lines/1 becomes shorter and simpler and no longer needs any ^ operators:
disjointed_lines(Ls) :-
setof((W, Z), disjoint_routes(W, Z), Ls).
The variables you don't want in the result of setof/3 are automatically hidden inside the auxiliary predicate definition.

Success and failure when querying the database

So I'm stuck on an exercise that I've been working on. I have the following facts:
sd(appleseed0, appleseed1).
sd(appleseed0, apple1).
sd(appleseed1, apple1).
sd(appleseed2, apple1).
sd(appleseed0, apple2).
sd(appleseed1, apple2).
sd(appleseed2, apple2).
What this means is that appleseed1 came from appleseed0, apple1 came from appleseed0, etc. The problem I'm having is that I need it to print out false if the values are switched around. Meaning, I want the query to result in "true" when the query is seed(appleseed0, apple1) and then "false" when the query is in opposite order like seed(apple1, appleseed0).
Right now, my predicate looks like this:
seed(A,B) :- sd(A,B) ; sd(B,A).
I understand that this is why my queries are returning true, no matter the order, but my only other idea is:
seed(A,B) :- sd(A,B).
but I cannot write it like that because that would make it an infinite loop with no false. How can I make it so that the query will result in "true" when shown with something like seed(appleseed2, apple2) and "false" when shown with something like seed(apple2, appleseed2)?
Hoping that I am reading your question correctly:
You don't need an extra predicate. Indeed, what you are looking for is the query:
?- sd(A, B).
This will succeed or fail just like you describe. The predicate
seed(A, B) :-
( sd(A, B)
; sd(B, A)
).
(just like yours, just formatted to be easier to follow) reads: "seed(A, B) is true when sd(A, B) is true. It is also true when sd(B, A) is true" (like you have noticed). An interesting side effect is that if you had these two facts in your database:
sd(foo, bar).
sd(bar, foo).
Then the query:
?- seed(foo, bar).
will succeed twice (!), just like the query
?- seed(bar, foo).
or the equivalent top level query
?- sd(bar, foo) ; sd(foo, bar).
That last query makes it most obvious why the query will succeed twice.
What confuses me: Why do you think that
seed(A, B) :-
sd(A, B).
will lead to an infinite loop? Is there some part of the program that you are not showing? As it stands, defining a predicate like this is equivalent to just giving sd/2 an alias, seed/2. This definition reads: "seed(A, B) is true when sd(A, B) is true."

Using "=" in Prolog

I'd like to know why I get an error with my SWI Prolog when I try to do this:
(signal(X) = signal(Y)) :- (terminal(X), terminal(Y), connected(X,Y)).
terminal(X) :- ((signal(X) = 1);(signal(X) = 0)).
I get the following error
Error: trabalho.pro:13: No permission to modify static procedure
'(=)/2'
It doesn't recognize the "=" in the first line, but the second one "compiles". I guess it only accepts the "=" after the :- ? Why?
Will I need to create a predicate like: "equal(x,y) :- (x = y)" for this?
Diedre - there are no 'functions' in Prolog. There are predicates. The usual pattern
goes
name(list of args to be unified) :- body of predicate .
Usually you'd want the thing on the left side of the :- operator to be a predicate
name. when you write
(signal(X) = signal(Y))
= is an operator, so you get
'='(signal(X), signal(Y))
But (we assume, it's not clear what you're doing here) that you don't really want to change equals.
Since '=' is already in the standard library, you can't redefine it (and wouldn't want to)
What you probably want is
equal_signal(X, Y) :- ... bunch of stuff... .
or
equal_signal(signal(X), signal(Y)) :- ... bunch of stuff ... .
This seems like a conceptual error problem. You need to have a conversation with somebody who understands it. I might humbly suggest you pop onto ##prolog on freenode.net or
some similar forum and get somebody to explain it.
Because = is a predefined predicate. What you actually write is (the grounding of terms using the Martelli-Montanari algorithm):
=(signal(X),signal(Y)) :- Foo.
You use predicates like functions in Prolog.
You can define something like:
terminal(X) :- signal(X,1);signal(X,0).
where signal/2 is a predicate that contains a key/value pair.
And:
equal_signal(X,Y) :- terminal(X),terminal(Y),connected(X,Y).

Resources