How do I show the results of pattern-matching goal

2019-04-11 20:13发布

问题:

Let's use the following prolog base :

father(anakinSkywalker, princessLeia).
father(anakinSkywalker, lukeSkywalker).
saysOhNo(lukeSkywalker).

sdesciencelover asked how to show the results of pattern-matching goals in swi-prolog from a shell invocation, and got an answer giving a manual transformation on the query, to isue a write.

swipl -q -s kb.pl -t "father(anakinSkywalker,X), writeln(X), false"

Result:

princessLeia
lukeSkywalker

This works fine when one only has a few queries with a single free variable, but manually transforming each one becomes tedious, and if we want proper output with the name of each variable along with its result, it soon becomes very annoying. For example to run the query father(AVariable, Another), one needs to write:

swipl -q -s kb.pl -t "father(AVariable,Another), write('AVariable='), write(AVariable), write(', Another='), writeln(Another), false"

Result:

AVariable=anakinSkywalker, Another=princessLeia
AVariable=anakinSkywalker, Another=lukeSkywalker

I tried to feed it the commands from a pipe, but it doesn't work great (I can't detect when it has finished writing the results, so it just hangs afterwards, and no newline separates the answers) :

(echo "father(X,Y)."; while true; do echo ";"; done) | swipl -q -s kb.pl

Result :

X = anakinSkywalker,
Y = princessLeia X = anakinSkywalker,
Y = lukeSkywalker.

swipl hangs here, and needs to be stopped with Control-C.

I know I could use a sed script to pre-process queries, adding the necessary code to print the variables in capital letters, but it would need a fair amount of work to work on complex queries, for example where two predicates must be satisfied :

father(X,Y), saysOhNo(Y).

To always give correct results, one would need to write a parser for prolog, which would be useless work since prolog already know how to do this interactively.

So here's my question : is there a way to tell GNU prolog or SWI prolog (or any other free version that can be easily installed on linux) to run some queries and print the results, just like they would do interactively, but without requiring me to type (or copy-paste) each query by hand ?

Edit : a way to store a series of queries in a file (either in the kb.pl file or an auxiliary file) and run them all, showing their results would be even better.

回答1:

You can use the command-line option --query-goal in GNU. Like so:

$ echo a| gprolog --query-goal 'X = 1 ; X =2'
GNU Prolog 1.4.1
By Daniel Diaz
Copyright (C) 1999-2012 Daniel Diaz
| ?- X = 1 ; X =2.

X = 1 ? a

X = 2

yes


回答2:

So far, here are the methods I found :

In gprolog

Using false's answer, I found that one must add a line at the top of the kb.pl file:

a(_) :- fail.

and then use ./query.sh kb.pl "father(X,Y), saysOhNo(Y)", where query.sh is:

#!/bin/sh
echo "a(fail)." | gprolog --query-goal "consult('$1'), $2"

When the query returns immediately (i.e. no results or a single result and gprolog managed to detect it was the last one), this will run the query consult('kb.pl'), actual_query., and then run the query a(fail). which will simply print an extraneous no on the console, thanks to the always-false predicate we added at the top of the file.

When gprolog asks what to do (i.e. several results, or a single result and gprolog couldn't detect it was the last one), this will run the query consult('kb.pl'), actual_query., read the a which asks gprolog to print all results, and then it will run the query (fail). which will simply print an extraneous no on the console, because these are just grouping parenthesis, so the query is equivalent to fail..

In xsb

One can use ./query.sh kb.pl "father(X,Y), saysOhNo(Y)", where query.sh is:

#!/bin/sh
(echo "consult('$1'), ${2%.}."; yes halt.) | xsb --noprompt --quietload --nobanner

When xsb asks what to do next, if the user types a non-empty string, followed by enter, it will print the next result, otherwise it will stop searching solutions to the current query. Therefore, with the yes halt. command, we type an infinite stream of non-empty lines. xsb will print all results to the query (each time reading halt., so as it is a non-empty string, it will continue with the next result), and return to its prompt. Then, the following halt. it receives will tell it to quit.

In swi-prolog

I haven't found a solution yet.

[rant]All this would be so much simpler, if the people building prolog implementations actually thought about using them non-interactively, like it's possible with most other languages.[/rant]



回答3:

You may have found a solution for your problem but anyway, here goes my approach. You can always recurse to the bagof built-in predicate. You may read what it does in the docs, this way you will learn more about it.

swipl -q -s starwars.pl -t "bagof(X, Y^father(X,Y), BagOfFathers), bagof(Y, X^father(X,Y), BagOfChildren), writeln(BagOfFathers), writeln(BagOfChildren)."
[anakinSkywalker,anakinSkywalker]
[princessLeia,lukeSkywalker]

You can also process it later as a mapping or whatever you want, the relations are 1:1 (No sure if is the correct way of stating it but I hope you get it)



回答4:

you can use the following bash script for swi-prolog:

#!/bin/sh

exec swipl -q  -f none -g "load_files([interface],[silent(true)])" \
         -t interface:get_args -- $*

this will load the file interface.pl and call the predicate get_args/0
to get the command line arguments you can call:

current_prolog_flag(argv, Arguments)

of course you can change the names of the predicates/files loaded.
the silent(true) arguments suppresses informational messages such as the intro text

edit: the error message you get is cause you probably dont have an interface.pl file (neither a get_args/0 predicate). you will have to replace interface with kb (or however you name the file) and interface:get_args with kb:father(X,Y), saysOhNo(Y) or use an auxiliary predicate within your prolog file such as run(X,Y):- father(X,Y), saysOhNo(Y) (which may be kinda cleaner)