Search code examples
prolog

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.


Solution

  • 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.