listrecursionprolognested-lists

How do I make a Prolog rule that only reads the lists inside lists?


I am somewhat new to Prolog, having only practised for a few months so far. I am stumped on the following problem and nothing I googled seemed to help.

I wrote the following code into a Prolog dictionary, in an attempt to make a rule which prints out the contents of lists nested inside of lists:

print_list([])      :- format('~n~n').
print_list([H | T]) :- format('~4f~80|~n', [H]),
print_list(T). 

is_list([], X)              :- X = false.
is_list([_Head | _Tail], X) :- X = true. 

list_foo([]). 
list_foo([ Head | Tail ]) :- 
    is_list(Head, X),
    ( X == true ->
      print_list(Head)
    ; X == false ->
      print(Head)
    ),
    list_foo([Tail | _Tail2]).

Having saved this to file and consulted it, the REPL returned 'true'. However, when trying this:

list_foo([7, 8, 9, [10, 20, 30, 40], 2, 3, [100, 200, 300, 400], 4, 5, 6]).

The REPL returned 'false', and when I tried the following:

list_foo([[10, 20, 30, 40], 2, 3, [100, 200, 300, 400], 4, 5, 6]).

I got the following output and error message:

> 10.0000                                                                         
> 20.0000                                                                         
> 30.0000                                                                         
> 40.0000                                                                         
> 
> 2.0000                                                                          
> 3.0000                                                                           
> ERROR: Type error: \`[]' expected, found `[100,200,300,400]' (a list)
> ("x" must hold one character) ERROR: In: ERROR:   [15]
> format('~4f~80|~n',[[100|...]]) ERROR:   [14]
> print_list([[100|...],4|...]) at d:/programming_stuff/mid-2025
> prolog/test.pl:2 ERROR:   [11] list_foo([[2|...]|_314]) at
> d:/programming_stuff/mid-2025 prolog/test.pl:12 ERROR:    [9]
> toplevel_call(user:user: ...) at c:/program
> files/swipl/boot/toplevel.pl:1173 ERROR:  ERROR: Note: some frames are
> missing due to last-call optimization. ERROR: Re-run your program in
> debug mode (:- debug.) to get more detail.

Trying to trace and to debug the code did not help me to figure out what was happening.

Can anyone please offer suggestions for making the code perform more as desired, or offer insights into what the code is doing here? This was intended as a self-imposed learning exercise rather than a part of production code or a university assignment, so any input at all would be appreciated.

edit:
Forgot to include needed information: I am using SWI-Prolog, version 9.0.4, AMD64, multi-threaded.


Solution

  • Since you are using SWI Prolog on Windows, use ?- gtrace. for the graphical tracer, it's more detailed and interactive than the inline text only one. Spacebar to step forwards, hover over the toolbar icons for other keyboard shortcuts.

    NB. you can click on the frames in the callstack, and then click the "retry selected goal" to skip backwards in the code and retry.[1]

    Query ?- list_foo([7, 8]). and it's quick to fail you may have to go through it a few times; pay close attention to the call is_list(Head, X) and the values shown in the Bindings window, and study which is_list/2 rule should match for those values.


    For the other one, the relevant error is:

    format('~4f~80|~n',[[100|...]]) ERROR:   [14]
                          ^
    

    Your code says:

    print_list([H | T]) :- format('~4f~80|~n', [H])
                                                ^
    

    it should only be printing the head of a list, but it's getting [100|...] - a nested list - as the head. I'll tell you where that's coming from, but put it in spoilers in case you want to chase down how the first list prints okay but the second one is getting nested...

    list_foo([Tail | _Tail2]). % should be list_foo(Tail).


    [1] Where you will see the same output printed again. This is why brebs is calling out "print-as-you-go"; Prolog can re-try code but it cannot un-print from the console. Ideally do some pure logic code which transforms data, e.g. turn a nested list into a flattened list, and then afterwards do impure printing/file/network code which changes the state of the world, e.g. printing the flattened list.