reading files in Prolog - prolog

I am trying to read the file 'nouns.csv' which looks like this in notepad:
femin,femin,1,f,woman,women.
aqu,aqu,1,f,water,waters.
I have tried using this in SWI-Prolog:
18 ?- read(X).
X = end_of_file.
19 ?- see('nouns.csv').
true.
20 ?- seeing(X).
X = <stream>(000000000017F770).
21 ?- read(X).
X = end_of_file.
22 ?-
I have absolutely no experience with Input and Output (in any language) and I am confused. I thought read(X) would return the whole file as a string (which I thought is a stream). I want to read in each line and apply this predicate to it:
nounstage(Input) -->
["noun("],
[Adjust],
[")."],
{
append(Adjust,[46],Input)
}.
nounlineparse(X,Y) :-
phrase(nounstage(X),N),
flatten(N,Y),
asserta(Y).
I presumed I would make a giant list of every line in the nouns.csv, then iterate through the list and apply nounlineparse to each element. How can I get my file/stream into this giant list (or is the giant list way a bad way of doing this?).

In SWI-Prolog, the easier route is library(csv).
If you need to do specific processing on read items, library(pure_input) can help you.
The basic skeleton is like
:- use_module(library(pure_input)).
:- use_module(library(dcg/basics)).
read_csv(Path, Records) :-
phrase_from_file(read_csv_file(Records), Path).
read_csv_file([Tokens|Lines]) -->
read_csv_line(Tokens), read_csv_file(Lines).
read_csv_file([]) --> [].
read_csv_line([Token]) -->
string(TokenS), ( ".\n" ; "." ), {atom_codes(Token, TokenS)} .
read_csv_line([Token|Tokens]) -->
string(TokenS), ",", {atom_codes(Token, TokenS)}, !, read_csv_line(Tokens).
You will need to pay much attention to details...

Related

How to join rules and print out outputs in prolog

I have list of facts as follows.
items(itemId('P01'),prodName('Pots'),stockQty(50),price(8200)).
items(itemId('P02'),prodName('Pans'),stockQty(50),price(400)).
items(itemId('P03'),prodName('Spoons'),stockQty(50),price(200)).
items(itemId('P04'),prodName('Forks'),stockQty(50),price(120)).
items(itemId('P05'),prodName('Kettles'),stockQty(50),price(500)).
items(itemId('P06'),prodName('Plates'),stockQty(50),price(60)).
How to print on the console something like the following when a command like print_all_products. is given.
..............
Available Products
..........
Name Qty
Pots 60
Pans 50
Spoons 40
..................
The Name and Qty must be properly formatted in a tabular structure.
I tried using forall and foreach I am unsuccessful in generating what i need.
Answer with more details is posted here.
Below is the code so that this is not a link only answer.
items(itemId('P01'),prodName('Pots'),stockOty(50),price(8200)).
items(itemId('P02'),prodName('Pans'),stockOty(50),price(400)).
items(itemId('P03'),prodName('Spoons'),stockOty(50),price(200)).
items(itemId('P04'),prodName('Forks'),stockOty(50),price(120)).
items(itemId('P05'),prodName('Kettles'),stockOty(50),price(500)).
items(itemId('P06'),prodName('Plates'),stockOty(50),price(60)).
header("\n........................\nAvailable Products\n........................\nName Qty\n").
footer("........................\n").
spaces(Length,Spaces) :-
length(List,Length),
maplist([_,0'\s]>>true,List,Codes),
string_codes(Spaces,Codes).
padded_string(String,Width,Padded_string) :-
string_length(String,String_length),
Padding_length is Width - String_length,
spaces(Padding_length,Padding),
atom_concat(String,Padding,Padded_string).
format_detail_line(item(Name,Quantity),width(Name_width),Formatted_item) :-
padded_string(Name,Name_width,Padded_name),
atom_concat(Padded_name,Quantity,Formatted_item).
add_detail_line(width(Name_Width),Item,Lines0,Lines) :-
format_detail_line(Item,width(Name_Width),Formatted_item),
atomic_list_concat([Lines0,Formatted_item,"\n"], Lines).
items_detail(Detail) :-
findall(item(Name,Quantity),items(_,prodName(Name),stockOty(Quantity),_),Items),
aggregate_all(max(Width),Width,(items(_,prodName(Name),_,_),string_length(Name,Width)),Name_Width),
Name_field_width is Name_Width + 1,
foldl(add_detail_line(width(Name_field_width)),Items,"",Detail).
print_all_products(Report) :-
header(Header),
items_detail(Detail),
footer(Footer),
atomic_list_concat([Header,Detail,Footer], Report).
print_all_products :-
print_all_products(Report),
write(Report).
:- begin_tests(formatted_report).
test(1) :-
print_all_products(Report),
with_output_to(atom(Atom),write(Report)),
assertion( Atom == '\n........................\nAvailable Products\n........................\nName Qty\nPots 50\nPans 50\nSpoons 50\nForks 50\nKettles 50\nPlates 50\n........................\n' ).
:- end_tests(formatted_report).
Note: The answer given by Peter is the customary way to do the formatting, but as I noted, that drives me nuts. Even so, that is the way I would do it in a production environment.
I gave this answer because the OP noted they were looking for a way to do it using predicates like forall/2 or foreach/2. Granted neither of them is used in this answer but the intent of using a more functional approach is used.
If the question was more open ended I would have given a answer using DCGs.
format/2 ... for putting things in neat columns, use ~|, ~t, ~+.
~| sets a tab to "here", ~t inserts fill characters, ~+ advances the tab beyond the last "here" (~|) and distributes the fill characters. So,
format("(~|~`.t~d~5+)~n", [123])
produces (..123) -- the format string right-justifies the number with .s in a width of 5, surrounded by parentheses.
You are asking for SQL-style tabular output and yes, that should be in the language as basic predicate set since when Reagan was prez. I don't know what's going on. It's probably out there in a library though (but where is the library?)
Meanwhile, here is the "failure driven-loop" using some of my personal toolbox goodies, but it uses SWI Prolog:
In file printthem.pl:
:- use_module(library('heavycarbon/strings/string_of_spaces.pl')).
:- use_module(library('heavycarbon/strings/string_overwriting.pl')).
items(itemId('P01'),prodName('Pots'),stockOty(50),price(8200)).
items(itemId('P02'),prodName('Pans'),stockOty(50),price(400)).
items(itemId('P03'),prodName('Spoons'),stockOty(50),price(200)).
items(itemId('P04'),prodName('Forks'),stockOty(50),price(120)).
items(itemId('P05'),prodName('Kettles'),stockOty(50),price(500)).
items(itemId('P06'),prodName('Plates'),stockOty(50),price(60)).
printthem :-
% ideally these should be built by getting max(length) over a column - hardcode for now!
string_of_spaces(5,SpacesId),
string_of_spaces(10,SpacesName),
string_of_spaces(4,SpacesQuant),
string_of_spaces(6,SpacesPrice),
% begin failure-driven loop!
items(itemId(Id),prodName(Name),stockOty(Quant),price(Price)), % backtrack over this until no more solutions
% transform data into string; see predicate format/2;
% capture output instead of letting it escape to STDOUT
with_output_to(string(TxtId),format("~q",[Id])),
with_output_to(string(TxtName),format("~q",[Name])),
with_output_to(string(TxtQuant),format("~d",[Quant])),
with_output_to(string(TxtPrice),format("~d",[Price])),
% formatting consist in overwriting the space string with the data-carrying string
string_overwriting(SpacesId,TxtId, 1,TxtIdFinal),
string_overwriting(SpacesName,TxtName, 1,TxtNameFinal),
string_overwriting(SpacesQuant,TxtQuant, 1,TxtQuantFinal),
string_overwriting(SpacesPrice,TxtPrice, 1,TxtPriceFinal),
% output the line
format("~s~s~s~s\n",[TxtIdFinal,TxtNameFinal,TxtQuantFinal,TxtPriceFinal]),
% close the loop
fail.
The above is just an ébauche. Improvements are possible in several distinct directions.
The modules loaded via
:- use_module(library('heavycarbon/strings/string_of_spaces.pl')).
:- use_module(library('heavycarbon/strings/string_overwriting.pl')).
can be obtained from GitHub here. You will have to grab several files and arrange them appropriately. Read the script load_and_test_script.pl. Don't mind the mess, this is work in progress.
If everything has been set up correctly:
?- [printthem].
true.
?- printthem.
'P01' 'Pots' 50 8200
'P02' 'Pans' 50 400
'P03' 'Spoons' 50 200
'P04' 'Forks' 50 120
'P05' 'Kettles' 50 500
'P06' 'Plates' 50 60
false.

Ask Prolog for predicates of an argument [duplicate]

another way to ask the question is:
How I can list all the properties of an atom?
For example:
movie(agora).
director(agora, 'Alejandro Amenabar')
duration(agora, '2h').
so, I will like to receive all the predicates that has agora for argument. In this case it will be: movie, director, duration, with the other parameters ('Alejandro Amenabar', '2h').
I found: this, and this questions, but I couldn't understand well.
I want to have the value of false in the "variable Answer" if PersonInvited doesn't like something about the movie.
My query will be:
answer(Answer, PersonInvited, PersonWhoMadeInvitation, Movie)
Answer: I don't like this director
answer(false, PersonInvited, PersonWhoMadeInvitation, Movie):-
director(Movie, DirectorName),not(like(PersonInvited,DirectorName)).
The same thing will happen with any property like genre, for example.
Answer: I don't like this genre
answer(false, PersonInvited, PersonWhoMadeInvitation, Movie):-
genre(Movie, Genre), not(like(PersonInvited,Genre)).
So, I want to generalize this situation, instead of writing repeatedly every feature of every object.
I found two solutions the 2nd is cleaner from my point of view, but they are different.
Parameters:
PredName: Name of the predicate.
Arity: The Arity of the Predicate.
ParamValue: If I want to filter by one specific parameter.
PosParam: Which is the position of the parameter in the predicate.
ListParam: All the value of the posibles values parameters (mustbe a Variable all the time).
Solution 1:
filter_predicate(PredName, Arity, ParamValue,PosParam, ListParam):-
current_predicate(PredName/Arity),
Arity >= PosParam,
nth(PosParam, ListParam, ParamValue),
append([PredName], ListParam, PredList),
GlobalArity is Arity + 1,
length(PredList, GlobalArity),
Predicate =.. PredList,
Predicate.
Query
filter_predicate(PredName, Arity, agora, 1, Pm).
Output
Arity = 2
Pm = [agora,'Alejandro Amenabar']
PredName = director ?
yes
Solution2:
filter_predicate(PredName, Arity, ParamList):-
current_predicate(PredName/Arity),
append([PredName], ParamList, PredList),
GlobalArity is Arity + 1,
length(PredList, GlobalArity),
Predicate =.. PredList,
Predicate.
Query 1:
filter_predicate(PredName, Arity, [agora, X]).
Output
Arity = 2
PredName = director
X = 'Alejandro Amenabar' ?
Query 2:
filter_predicate(PredName, Arity, [X, 'Alejandro Amenabar']).
Output
Arity = 2
PredName = director
X = agora ?
here is my attempt, using SWI-Prolog
?- current_predicate(so:F/N), N>0, length(As,N), Head =.. [F|As], clause(so:Head,Body), As=[A|_], A==agora.
note that I coded into a module called so the facts, so I qualify with the module name the relevant calls. Such builtins (clause/2 and current_predicate/1) are ISO compliant, while modules (in SWI-prolog) are not. So I'm not sure about portability, etc...
clause/2 it's a builtin that allows for easy writing metainterprets. See the link for an awesome introduction to this Prolog historical 'point of strength'.
The 2 last calls (I mean, As=[A|_], A==agora) avoid matching clauses having a variable as first argument.
Using reading lines into lists with prolog
All your predicates are in a file 'my_file.pl'.
e.g. my_file.pl contains:
movie(agora).
director(agora, 'Alejandro Amenabar').
duration(agora, '2h').
You can use:
getLines(File,L):-
setup_call_cleanup(
open(File, read, In),
readData(In, L),
close(In)
).
readData(In, L):-
read_term(In, H, []),
( H == end_of_file
-> L = []
; L = [H|T],
readData(In,T)
).
pred_arg_file(Pred,Argue,File):-
getLines(File,L),
member(M,L),
M=..List,
member(Argue,List),
List=[Pred|_].
Then you can query:
?-pred_arg_file(Pred,agora,'my_file.pl').
Pred = movie ;
Pred = director ;
Pred = duration ;
false
or
?- findall(Pred,pred_arg_file(Pred,agora,'my_file.pl'),Preds).
Preds = [movie,director,duration].
If you want to return the properties, return the whole List not just the head.
pred_arg_file(List,Argue,File):-
getLines(File,L),
member(M,L),
M=..List,
member(Argue,List).
From my understanding you should change your data representation so that you can query the relations.As other answers have pointed out, So use triples, you can easily write code to change all your relations into this form as a one off. You then need to work out what the best way to store likes or dislikes are. This will effect how negation works. In this example:
relation(starwars,is,movie).
relation(lucas, directs,starwars).
relation(agora, is,movie).
relation('Alejandro Amenabar', directs, agora).
relation(agora, duration, '2h').
like(ma,'Alejandro Amenabar').
like(ma,movie).
like(ma,'2h').
ma_does_not_want_to_go(Film):-
relation(Film,is,movie),
relation(Film,_,Test), \+like(ma,Test).
ma_does_not_want_to_go(Film):-
relation(Film,is,movie),
relation(Test,_,Film), \+like(ma,Test).
ma_wants_to_go(Film):-
relation(Film,is,movie),
\+ma_does_not_want_to_go(Film).
sa_invites_ma(Film,true):-
ma_wants_to_go(Film).
sa_invites_ma(Film,false):-
ma_does_not_want_to_go(Film).
A draft of a solution using Logtalk with GNU Prolog as the backend compiler:
% a movie protocol
:- protocol(movie).
:- public([
director/1,
duration/1,
genre/1
]).
:- end_protocol.
% a real movie
:- object('Agora',
implements(movie)).
director('Alejandro Amenabar').
duration(120).
genre(drama).
:- end_object.
% another real movie
:- object('The Terminator',
implements(movie)).
director('James Cameron').
duration(112).
genre(syfy).
:- end_object.
% a prototype person
:- object(person).
:- public([
likes_director/1,
likes_genre/1
]).
:- public(likes/1).
likes(Movie) :-
conforms_to_protocol(Movie, movie),
( Movie::genre(Genre),
::likes_genre(Genre) ->
true
; Movie::director(Director),
::likes_director(Director) ->
true
; fail
).
:- end_object.
% a real person
:- object(mauricio,
extends(person)).
likes_director('Ridlye Scott').
likes_genre(drama).
likes_genre(syfy).
:- end_object.
Some sample queries:
$ gplgt
...
| ?- {movies}.
...
(5 ms) yes
| ?- mauricio::likes('Agora').
true ?
yes
| ?- mauricio::likes(Movie).
Movie = 'Agora' ? ;
Movie = 'The Terminator' ? ;
no
| ?- 'The Terminator'::director(Director).
Director = 'James Cameron'
yes
The code can be improved in several ways but it should be enough to give you a clear idea to evaluate this solution.
If I understood your question properly I propose the follow:
What if you change your schema or following this idea you can make a method that simulate the same thing.
class(movie, agora).
property(director, agora, 'Alejandro Amenabar').
property(duration, agora, '2h').
If do you want the types of agora, the query will be:
class(Type, agora)
If you want all the properties of agora, that will be:
property( PropertyName, agora, Value).

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.

print customized result in prolog

I am working on a simple prolog program. Here is my problem.
Say I already have a fact fruit(apple).
I want the program take a input like this ?- input([what,is,apple]).
and output apple is a fruit
and for input like ?-input([is,apple,a,fruit])
instead of default print true or false, I want the program print some better phrase like yes and no
Can someone help me with this?
My code part is below:
input(Text) :-
phrase(sentence(S), Text),
perform(S).
%...
sentence(query(Q)) --> query(Q).
query(Query) -->
['is', Thing, 'a', Category],
{ Query =.. [Category, Thing]}.
% here it will print true/false, is there a way in prolog to have it print yes/no,
%like in other language: if(q){write("yes")}else{write("no")}
perform(query(Q)) :- Q.
In Prolog there is a construct if/else:
perform(query(Q)) :-
( Q
-> write(yes:Q)
; write(no)
), nl.
When I need a stricter control on output formatting, I use format.
Not very friendly, but offers most of the usual options...

Prolog remove character spaces from atom for using term_to_atom

At some point of my program I have an atom formed by what previously were also atoms, and I want to remove the character spaces within it so that later I can use without any problem:
term_to_atom(Result, 'second2(second2),region(ºMediterranean Sea),months(no_value),third3(third3),recog(ºNew Type),distance(no_value)').
and obtain this
Result = (second2(second2), region(ºMediterraneanSea), months(no_value), third3(third3), recog(ºNewType), distance(no_value))
or also the original would work
Result = (second2(second2), region(ºMediterranean Sea), months(no_value), third3(third3), recog(ºNew Type), distance(no_value))
because if I don't delete those character spaces then term_to_atom will complain about it. How can I solve it?
You can use this procedure:
strip_spaces(S, NoSpaces) :-
atom_codes(S, Cs), delete(Cs, 0' , X), atom_codes(NoSpaces, X).
but delete/3 is deprecated. Another possibility is
strip_spaces(S, NoSpaces) :-
atomic_list_concat(L, ' ', S),
atomic_list_concat(L, NoSpaces).
Either of these will 'eat' each space, but from your problem description, in comments you exchanged with gusbro, this doesn't seems to me the right way to go. Changing the literals seems at DB interface could ask for trouble later.
Parsing your input to a list, instead of a conjunction, can be done with DCGs:
:- [library(http/dcg_basics)].
parse_result(X, R) :-
atom_codes(X, Cs),
phrase(parse_list(R), Cs).
parse_list([T|Ts]) -->
parse_term(T), (",", parse_list(Ts) ; {Ts=[]}).
parse_term(T) -->
string(F), "(", string(Arg), ")",
{atom_codes(Fa,F), atom_codes(Arga,Arg), T =.. [Fa,Arga]}.

Resources