How do I Prolog specify pair printing format - prolog

Pair data is nice to work with, but I found hard to present. I ask how to accomplish printing a set of vectors such that keys & values line up in a pleasantly. Leading zeros in the pair-values, would help. Sample data:
[1-7,2-43,3-56,4-87,5-110,6-80,7-15]
[1-1837,2-1873,3-1911,4-1946,5-1975,6-1994,7-2005]
I tried to figure out use of SWI format_predicate ; but couldn't.
Then I thought to experiment inline;
format('~n~w ~w~w~n', ['Pairs: ',1-246,1-2, ' EOL']).
End result should deal with pairs of the form KK-VVVV:
01-0007 02-0043 03-0056 04-0087 05-0110 06-0080 07-0015 398 People 7 Gens.
01-1837 02-1873 03-1911 04-1946 05-1975 06-1994 07-2005 Spanning 168 Years
Final Answers:
fpair(A-B) :- format('~`0t~d~2|-~`0t~d~7| ', [A,B])
applist(_,[]). applist(P,[X|L]) :- Q =.. [P,X],call(Q),applist(P,L).
dojustone(X):- format('~# ',[fpair(X)]).
dolist(X):- applist(dolist,X).

I use a # specifier for complex formats, it allows to output specific terms. For instance
?- format('~s~n~#~n~#~n~w~n', ['Pairs: ',fpair(1-246),fpair(1-2), ' EOL']).
that is, fpair/1 is an user predicate, called by #, capturing its output.
To get fixed width fields, I use the tab specification, built from two specifiers working together. Finally, to prefix with 0s, I would use
fpair(A-B) :-
format('~`0t~d~6| ~`0t~d~12|', [A,B]).
Without knowing a priori the maximum number of digits, we must use a guess. I used 6 here.

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.

Pattern matching using list of characters

I am having difficulty pattern matching words which are converted to lists of characters:
wordworm(H1,H2,H3,V1,V2) :-
word(H1), string_length(H1,7),
word(H2), string_length(H2,5),
word(H3), string_length(H3,4),
word(V1), string_length(V1,4),
word(H3) \= word(V1),
atom_chars(H2, [_,_,Y,_,_]) = atom_chars(V1, [_,_,_,Y]),
word(V2), string_length(V2,5),
word(H2) \= word(V2),
atom_chars(H3, [_,_,_,Y]) = atom_chars(V2, [_,_,_,_,Y]).
Above this section, I have a series of 600 words in the format, word("prolog"). The code runs fine, without the atom_chars, but with it, I get a time-out error. Can anyone suggest a better way for me to structure my code?
Prolog predicate calls are not like function calls in other languages. They do not have "return values".
When you write X = atom_chars(foo, Chars) this does not execute atom_chars. It builds a data structure atom_chars(foo, Chars). It does not "call" this data structure.
If you want to evaluate atom_chars on some atom H2 and then say something about the resulting list, call it like:
atom_chars(H2, H2Chars),
H2Chars = [_,_,Y,_,_]
So overall maybe your code should look more like this:
...,
atom_chars(H2, H2Chars),
H2Chars = [_,_,Y,_,_],
atom_chars(V1, V1Chars),
V1Chars = [_,_,_,Y],
...
Note that you don't need to assert some kind of "equality" between these atom_chars goals. The fact that their char lists share the same variable Y means that there will be a connection: The third character of H2 must be equal to the fourth character of V1.

Format on Prolog

I have a doubt making tables with format, I need to make tables, I know I can make it this way:
If for example my table is tabla("estudents",["name","age","id"]).
But I have a problem, I need to get the numbers of attributes of the table, then I'll set a length of 18 to each square and the length will be N..
print_table_name(C):- tabla(C,A), //I SEARCH MY TABLE
atom_codes(Name,C), //PASSING THE NAME TO ATOM
length(A,N), //I GET MY NUMBER OF ATRIBUTES
Length is 18*N, //Length WILL BE THE LENGTH OF THE TABLE
print_edge(N), //HERE I PRINT THE TOP EDGE
format('|~t~a~t~N|)|~n',Name), //HERE IS MY ERROR
print_edge(N). //HERE I PRINT THE BOTTOM EDGE
print_edge(0):- format('~n',[]).
print_edge(N):- format('+~`-t~18|+', []), M is N-1, print_edge(M), !.
format('|~t~a~t~N|)|~n',Name) here I can't pass N as a variable, then I dont know how I can do to format get the N, N is the length of the table..
It print this
+--------------------------------------------------------------------------+
|students
||
+--------------------------------------------------------------------------+
and if I put the length where is N, then it works.
+--------------------------------------------------------------------------+
| students |
+--------------------------------------------------------------------------+
The problem is that I don't know how to pass the variable N to format.
In this case you should pass N as an argument to the format/2 predicate. Replace the variable N in the format by the symbol * and put N in the argument list.
I'm not sure if you will get the desire effect, but at least it won't fail.
format('|~t~a~t~*|)|~n',[Name, N]).
Edit
Right now I can only test the solution in this limited online interpreter: I replace the ~t by the character dot ~46t to see the effect and this is the result:
?- format('|~46t~a~46t~*|)|~n',['students', 72]).
|...............................students................................)|
PS: Are you sure about the parenthesis between the two last vertical bar?

Parse To Prolog Variables Using DCG

I want to parse a logical expression using DCG in Prolog.
The logical terms are represented as lists e.g. ['x','&&','y'] for x ∧ y the result should be the parse tree and(X,Y) (were X and Y are unassigned Prolog variables).
I implemented it and everything works as expected but I have one problem:
I can't figure out how to parse the variable 'x' and 'y' to get real Prolog variables X and Y for the later assignment of truth values.
I tried the following rule variations:
v(X) --> [X].:
This doesn't work of course, it only returns and('x','y').
But can I maybe uniformly replace the logical variables in this term with Prolog variables? I know of the predicate term_to_atom (which is proposed as a solution for a similar problem) but I don't think it can be used here to achieve the desired result.
v(Y) --> [X], {nonvar(Y)}.:
This does return an unbound variable but of course a new one every time even if the logical variable ('x','y',...) was already in the term so
['X','&&','X'] gets evaluated to and(X,Y) which is not the desired result, either.
Is there any elegant or idiomatic solution to this problem?
Many thanks in advance!
EDIT:
The background to this question is that I'm trying to implement the DPLL-algorithm in Prolog. I thought it would by clever to directly parse the logical term to a Prolog-term to make easy use of the Prolog backtracking facility:
Input: some logical term, e.g T = [x,'&&',y]
Term after parsing: [G_123,'&&',G_456] (now featuring "real" Prolog variables)
Assign a value from { boolean(t), boolean(f) } to the first unbound variable in T.
simplify the term.
... repeat or backtrack until a assignment v is found so that v(T) = t or the search space is depleted.
I'm pretty new to Prolog and honestly couldn't figure out a better approach. I'm very interested in better alternatives! (So I'm kinda half-shure that this is what I want ;-) and thank you very much for your support so far ...)
You want to associate ground terms like x (no need to write 'x') with uninstantiated variables. Certainly that does not constitute a pure relation. So it is not that clear to me that you actually want this.
And where do you get the list [x, &&, x] in the first place? You probably have some kind of tokenizer. If possible, try to associate variable names to variables prior to the actual parsing. If you insist to perform that association during parsing you will have to thread a pair of variables throughout your entire grammar. That is, instead of a clean grammar like
power(P) --> factor(F), power_r(F, P).
you will now have to write
power(P, D0,D) --> factor(F, D0,D1), power_r(F, P, D1,D).
% ^^^^ ^^^^^ ^^^^
since you are introducing context into an otherwise context free grammar.
When parsing Prolog text, the same problem occurs. The association between a variable name and a concrete variable is already established during tokenizing. The actual parser does not have to deal with it.
There are essentially two ways to perform this during tokenization:
1mo collect all occurrences Name=Variable in a list and unify them later:
v(N-V, [N-V|D],D) --> [N], {maybesometest(N)}.
unify_nvs(NVs) :-
keysort(NVs, NVs2),
uniq(NVs2).
uniq([]).
uniq([NV|NVs]) :-
head_eq(NVs, NV).
uniq(NVs).
head_eq([], _).
head_eq([N-V|_],N-V).
head_eq([N1-_|_],N2-_) :-
dif(N1,N2).
2do use some explicit dictionary to merge them early on.
Somewhat related is this question.
Not sure if you really want to do what you asked. You might do it by keeping a list of variable associations so that you would know when to reuse a variable and when to use a fresh one.
This is an example of a greedy descent parser which would parse expressions with && and ||:
parse(Exp, Bindings, NBindings)-->
parseLeaf(LExp, Bindings, MBindings),
parse_cont(Exp, LExp, MBindings, NBindings).
parse_cont(Exp, LExp, Bindings, NBindings)-->
parse_op(Op, LExp, RExp),
{!},
parseLeaf(RExp, Bindings, MBindings),
parse_cont(Exp, Op, MBindings, NBindings).
parse_cont(Exp, Exp, Bindings, Bindings)-->[].
parse_op(and(LExp, RExp), LExp, RExp)--> ['&&'].
parse_op(or(LExp, RExp), LExp, RExp)--> ['||'].
parseLeaf(Y, Bindings, NBindings)-->
[X],
{
(member(bind(X, Var), Bindings)-> Y-NBindings=Var-Bindings ; Y-NBindings=Var-[bind(X, Var)|Bindings])
}.
It parses the expression and returns also the variable bindings.
Sample outputs:
?- phrase(parse(Exp, [], Bindings), ['x', '&&', 'y']).
Exp = and(_G683, _G696),
Bindings = [bind(y, _G696), bind(x, _G683)].
?- phrase(parse(Exp, [], Bindings), ['x', '&&', 'x']).
Exp = and(_G683, _G683),
Bindings = [bind(x, _G683)].
?- phrase(parse(Exp, [], Bindings), ['x', '&&', 'y', '&&', 'x', '||', 'z']).
Exp = or(and(and(_G839, _G852), _G839), _G879),
Bindings = [bind(z, _G879), bind(y, _G852), bind(x, _G839)].

Illegal start of term in Prolog

I'm trying to write some predicates to solve the following task (learnprolognow.com)
Suppose we are given a knowledge base with the following facts:
tran(eins,one).
tran(zwei,two).
tran(drei,three).
tran(vier,four).
tran(fuenf,five).
tran(sechs,six).
tran(sieben,seven).
tran(acht,eight).
tran(neun,nine).
Write a predicate listtran(G,E) which translates a list of German number words to the corresponding list of English number words. For example:
listtran([eins,neun,zwei],X).
should give:
X = [one,nine,two].
I've written:
listtran(G,E):- G=[], E=[].
listtran(G,E):- G=[First|T], tran(First, Mean), listtran(T, Eng), E = [Mean|Eng).
But I get the error: "illegal start of term" when compiling. Any suggestions?
The last bracket in your last line should be a square one.
Also, you might want to make use of Prolog's pattern matching:
listtran([], []).
listtran([First|T], [Mean|EngT]):-
tran(First, Mean),
listtran(T, EngT).

Resources