I have a Node.JS application that I want to do the following with:
1. Queries an API to read clauses and writes them to a file data.pro
2. Runs swipl as a command to output the result
3. Parses the result and continues the Node.JS execution.
I already have the swipl command as:
swipl -s triangular.pro -g "customRatio(A,C,D,1.05,T)." -t halt.
Now customRatio/5 has a ton of solutions that I would like to process further in my main application. An example output of running this in interactive mode would be:
A = (portugal, brazil, 656.1249261859458),
B = (brazil, germany, 5.36135535063264),
C = (germany, portugal, 0.0002993),
T = 1.0528532618885567 ;
I don't need it to be in this format, I just want the most straightforward way to get all the goals from adding something either to the command or my program. I've seen examples with findAll but couldn't get it to work with multiple outputs to the stream. I've also seen examples with dump and write.
I'm just thinking there must be a trivial way to just dump all the results to a file.
Thank you in advance.
Could your please elaborate, which problems did you encounter with forall?
The forall/2 based solution I can think is as follows (I've split it to multiple lines so it's more readable):
forall(
(Goal = customRatio(A, C, D, 1.05, T), call(Goal)),
(write(Goal), nl)
)
The command becomes:
swipl -s triangular.pro -g "forall((Goal = customRatio(A, C, D, 1.05, T), call(Goal)), (write(Goal), nl))." -t halt.
PS: you can put forall into helper predicate which takes Goal as an argument and then writes all results to screen or file, e.g.:
swipl -s triangular.pro -g "results_to_file(customRatio(A, C, D, 1.05, T))." -t halt.
So with the help of the comments from #DmitriChubarov and a similar reply from #code_x386 I got there.
Assuming we have a function that already provides the results:
customRatio(A,C,D,1.05,T)
We want all results for the Goal and write them to a file. I've done that using:
findOpportunities(MinRatio):-
open('output.txt',write, Stream),
findall((P1,P2,P3,Ratio),customRatio(P1,P2,P3, MinRatio, Ratio), List),
write(Stream, List),
close(Stream).
And then calling the following on the command line:
swipl -s triangular.pro -g "findOpportunities(1.02)." -t halt.
The solution proposed by code_x386 works but writes the clause as well as the results, for example:
customRatio((portugal,brazil,1707.3295658260913),(brazil,germany,0.03409),(germany,portugal,0.017399),1,1.0126716463779004)
customRatio((brazil,germany,0.03409),(germany,france,0.001601),(france,brazil,18663.68047779022),1,1.018628032848078)
customRatio((brazil,germany,0.03409),(germany,portugal,0.017399),(portugal,brazil,1707.3295658260913),1,1.0126716463779004)
Thank you both!
Related
I setup the following rules to find if there is a relationship between two elements:
directReference(A,B) :- projectReferences(A,B).
transitiveReference(A,C) :- directReference(A,B),directReference(B,C).
transitiveReferenceD1(A,D) :- transitiveReference(A,C),directReference(C,D).
transitiveReferenceD2(A,E) :- transitiveReferenceD1(A,D),directReference(D,E).
Can I write a PrologScript that will check all these queries for a fact? Although I plan to use Ruby&Rake, someone is trying to do a non-interactive call from PHP here and it has not worked. I also saw this answer and tried Kaarel's answer. I just added a new opts_spec:
opts_spec(
[ [opt(day), type(atom),
shortflags([d]), longflags(['term', 'day']),
help('name of day')]
, [opt(goal),
shortflags([g]), longflags([goal]),
help('goal to be called')]
, [opt(projectReferences), type(atom),
shortflags([pr]), longflags(['term', 'projectReferences']),
help('Project Reference lookup')]
]
).
I then compiled with:
.\swipl.exe -o day.exe -g main -c "D:\DevProjects\AskJoe\Output\Sample.pro"
And ran it with:
./day.exe -g "day(Sunday)"
And got error:
ERROR: Prolog initialisation failed: ERROR: validate_opts_spec/1:
Domain error: unique_atom' expected, foundterm' (ambiguous flag)
My goal is to have this work:
./day.exe -g "transitiveReference('a','b')"
I don't like compiling a "day.exe" to run a script (according to the docs this often is not necessary), but I have found no other way to pass arguments to rules.
I saw a basic intro on swi-pl.org that has not helped much. It does not explain how to make the leap from the script.sh file example to the execution of ./eval 1+2. In fact, the example is a comment so I'm totally lost
Here is a very crude example of a PrologScript program that will read its arguments as a single goal (which may be compound), call it, and then terminate. It should work on *nix systems, and has been tested on OS X. It is just a slight variation of the example program given for using PrologScript in the SWI docs:
#!/usr/bin/env swipl
:- initialization main.
query :-
current_prolog_flag(argv, Argv),
concat_atom(Argv, ' ', Atom),
read_term_from_atom(Atom, Term, []),
call(Term).
main :-
catch(query, E, (print_message(error, E), fail)),
halt.
main :-
halt(1).
projectReferences(valueA, valueB) :- writeln('I was called!').
directReference(A,B) :- projectReferences(A,B).
transitiveReference(A,C) :- directReference(A,B),directReference(B,C).
transitiveReferenceD1(A,D) :- transitiveReference(A,C),directReference(C,D).
transitiveReferenceD2(A,E) :- transitiveReferenceD1(A,D),directReference(D,E).
After saving this file as, e.g., cli_test.pl, you'll need to change the permissions on the file so that the operating system will recognize it as an executable:
chmod -x scratchboard.pl
After that, you should be able to call the file as as a normal executable from the command line:
$ path/to/the/file/scratchboard.pl 'transitiveReferenceD1(A,D).'
I was called!
Note:
The goal to be evaluated is simply passed as a single argument. query/0 will then retrieve this argument using current_prolog_flag/2, read it as a Prolog term, and call it.
Since the program is not running in interactive mode, the only output will result from explicit imperatives to write out, such as occur if catch/3 (in the body of main/0) is triggered by an error or if projectReferences/2 is called successfully.
Using library(optparse) seems advisable for more complicated cli interface, but is not necessary for your stated aim of merely querying goals in a file.
I understand that getting the PrologScript approach to work on Windows is somewhat different. A bit of information can be fond here: http://www.swi-prolog.org/FAQ/PrologScript.html
So I got this all working and then after a few runs everything just stopped. I started getting 'permission denied bad interpreter' errors. All I can say is that it has something to do with the hashBang. The workaround for me was to create a shell script around the call to swipl:
shellscript.sh
#!/bin/bash
swipl -s script4.pl 'projectReferences(A,D).'
Then I continued using aBathologist's example, but just took off the hashBang:
:- initialization main.
query :-
current_prolog_flag(argv, Argv),
concat_atom(Argv, ' ', Atom),
read_term_from_atom(Atom, Term, []),
call(Term).
main :-
catch(query, E, (print_message(error, E), fail)),
halt.
main :-
halt(1).
projectReferences(valueA, valueB) :- writeln('I was called!').
directReference(A,B) :- projectReferences(A,B).
transitiveReference(A,C) :- directReference(A,B),directReference(B,C).
transitiveReferenceD1(A,D) :- transitiveReference(A,C),directReference(C,D).
transitiveReferenceD2(A,E) :- transitiveReferenceD1(A,D),directReference(D,E).
So, I have some Prolog code that models variable assignments, such as in a programming language, and tries to see if they are compatible with one another. So,
assigned(X, X).
and(P, Q) :- P, Q.
or(P, Q) :- P.
or(P, Q) :- Q.
% and then we should test an expression like this:
and(assigned(X, 5), assigned(X, 6)).
So that last expression fails, since X can't be assigned to both 5 and 6.
Now, what I want to do is have this test a single expression (which can and should be hardcoded into the file), and then simply print out whether or not it's able to be satisfied. But it seems that SWIPL really wants me to run interactively. Ideally, something like this:
> ./test-assignments
false.
Surely this is possible? I'm at my wit's end on this.
There are several ways to get an SWI-Prolog program to run directly from the shell. You can look at this question and the answers:
How to run SWI-Prolog from the command line?
My personal preference now is to have a file example.pl like this:
:- set_prolog_flag(verbose, silent).
:- initialization(main).
main :-
format('Example script~n'),
current_prolog_flag(argv, Argv),
format('Called with ~q~n', [Argv]),
halt.
main :-
halt(1).
which I then run from the command line with:
$ swipl example.pl and a few arguments
Example script
Called with [and,a,few,arguments]
The initialization directive tells the interpreter which goal to evaluate after it loads the program: here it is main/0. The name main is a convention, it could have been called anything else.
See the linked question and answers for other options.
I am using swi-prolog version: 6.3.11. And there is no ~/.plrc. swipl -q starts VERY slow and needs more RAM given by top:
size:315M RES:210M
?- listing.
:- thread_local thread_message_hook/3.
:- dynamic thread_message_hook/3.
:- volatile thread_message_hook/3.
However, swipl -q -f foo.pl starts VERY quickly and needs few RAM:
size:11160K RES:4236K
I am building pl-devel on FreeBSD 9. (I guess swipl -q uses system wide initial file which i cannot find ? )
Regards!
Couple of thoughts for you:
What happens if you place the contents of your foo.pl file into a new .plrc file in your home dir ?
What happens if you perform "swiple -q -f none"
I don't know the terminology; but I guess there must be a command to show all the rules that have been defined ... If you run the slow swiple command, can you compare how many rulesthere are compared to how many you get when you run with the -f parameter ?
I have successfully connected PHP with Prolog and managed to query the desired knowledge base that resides in knowledge_base.pl file and managed to echo the results via php exec function.
I encountered a problem in echoing the true/false value that Prolog returns after each query execution (see previous question) so I came up with a solution that I have trouble implementing.
Let's assume I have a simple knowledge_base.pl file with these facts and rules:
girl(erin).
boy(john).
likes(erin, reading).
likes(john, reading).
hangs_out_with(erin, X) :-
likes(X, reading),
boy(X),
writeln('Someone s got a new friend!!').
Lets say that I want to see if erin is a girl and if so, write that it is true, or else write that it is false. I added this to my knowledge_base.pl file:
girl(erin) :-
girl(erin)
-> write('it is true')
; write('it is not true').
When I enter the query: ?- girl(erin). I get an out of local stack error. I searched the Web and found out that this is due to infinite recursion.
Can someone give me a hint in how to write
girl(X) :-
( girl(X)
-> write('it is true')
; write('it is not true')).
in Prolog? Thanks in advance.
As a new user I'm not allowed to post pictures.
SWI-Prolog's output:
1 ?-hangs_out_with(erin,kosta).
false.
2 ?-hangs_out_with(erin,john).
Someone s got a new friend!!
true.
Command prompt's output:
C:\(directory)>swipl -q -f knowledge_database.pl -g hangs_out_with(erin,kosta),halt.
1 ?-halt. (the halt is inputted by me.)
C:\(directory)>swipl -q -f knowledge_database.pl -g hangs_out_with(erin,john),halt.
Someone s got a new friend!!
The first query fails and the second succeds. As you can see, prolog after the query executions outputs true/false but when i execute the same query in command prompt the true/false values do not get echoed!!
I think you should ask
is_girl(X) :-
girl(X) -> write('t is true') ; write('it is not true').
EDIT
Do you mean this kind of thing ?
is_type(Type, X) :-
call(Type, X) -> writeln(yes); writeln(no).
so you "connect PHP to Prolog" by executing a Prolog query in command shell and capturing and analyzing its output. http://www.swi-prolog.org/man/quickstart.html says
"2.1.1.1 Starting SWI-Prolog on Unix
By default, SWI-Prolog is installed as 'swipl'. The command-line arguments of SWI-Prolog itself and its utility programs are documented using standard Unix man pages."
So do consult a man page about "-q" switch. Is it the "-q" for quiet perhaps? What does "-f" mean (ok, that's probably "file")? But the solution is the same - just use a different name for the new predicate.
Notice that in your first attempt,
C:>swipl -q -f knowledge_database.pl -g hangs_out_with(erin,kosta),halt.
1 ?-halt. (the halt is inputted by me.)
halt isn't executed, precisely because hangs_out_with(erin,kosta) has failed. A comma signifies conjunction (an "and").
All you need to do is create a new predicate that reports whether the goal is true or false, and succeeds always:
report_hangs_out_with(A,B):-
hangs_out_with(A,B)- > writeln(['YES',A,B]) ; writeln('NO').
and use it instead:
C:>swipl -q -f knowledge_database.pl -g report_hangs_out_with(erin,kosta),halt.
Also, Prolog echoing "true" or "false" is part of its interactive session. But you terminate it with halt!
edit: you posted:
1 ?-hangs_out_with(erin,kosta).
false.
So, when you run that query in interactive Prolog shell, it reports the failure. halt/0 exits the Prolog shell. When you run it with a goal specified through command line switch, apparently it does not report the success of failure. That's the fact of nature as far as we users are concerned (a.o.t. the compiler writers). It is easily addressable with what I've shown you. And you yourself say that it works, too. For each predicate that can fail or succeed, define another, reporting predicate, just as I've shown you.
Here's a sample transcript (in Windows, but that's irrelevant). That should clear up your doubts:
C:\Program Files\pl\bin>plcon -q -g (writeln('****'),halt).
**** // didn't say Yes, right??
C:\Program Files\pl\bin>plcon -q
1 ?- writeln('****'),halt.
**** // didn't say Yes here either
C:\Program Files\pl\bin>plcon -q
1 ?- writeln('****').
****
Yes // does say Yes as part of interaction
2 ?- halt.
C:\Program Files\pl\bin>
So that's the way it is. Deal with it. :) Or write Jan and ask him to change it. :)
girl(erin) :-
girl(erin)
-> write('it is true')
; write('it is not true').
This is wrong for two reasons. Prolog tries to resolve the body taking the left-most literal.
So it basically goes in a loop "is erin girl? yes, if erin is girl. Is erin girl?..."
The second reason is you are mixing two different things. Try to keep your knowledge representation part separated from the way you use it.
In Prolog you just say:
girl(erin)
And then query
?- girl(erin)
Prolog will just say "yes". If you want to print it, probably the easiest way is adding a predicate.
check_and_print(X) :- X, write(X), write(" is true").
Probably you need a call(X) instead of X, depending on the implementation you are using.
I'm not sure about the command prompt out, I suspect the outcome is returned in a different way.
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.
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]
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
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)
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)