How to join rules and print out outputs in prolog

Viewed 285

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.

3 Answers

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.

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.

Related