How to use this encoded data structure?

I can not understand how to use dt here. https://stackoverflow.com/a/41408523

If I try for example: dt(indian, X). I get false. And if I try dt(X, Y).

I get

X = food_type,
Y = [(indian->dt(spicy, [(y->curry), (n->curma)])), (chinese->dt(fry, [(y->stirFry), (n->chicken)])), (malay->dt(chili, [(y->sambal), (n->singgang)]))].

I quite don’t understand how to use ifs inside the list or how to traverse this dt. I would be really happy if you could kindly help me here.

Thanks in advance

If I try for example: dt(indian, X). I get false.

Well… there is really only one toplevel definition of dt, and it is:

dt(food_type,
    [indian  -> dt(spicy, [ y -> curry,   n -> curma ])
    ,chinese -> dt(fry,   [ y -> stirFry, n -> chicken ])
    ,malay   -> dt(chili, [ y -> sambal,  n -> singgang ])
    ]).

…which has food_type as its first argument, so dt(indian, X) isn’t going to match anything, hence the false.

And if I try dt(X, Y). […]

You’ll notice that Prolog only produces one answer, with X = food_type as the first argument, which reflects what we just saw. You’ll also see that Y is a list. When you look at dt, you’ll see that the first argument is an atom, and the second one is a list with elements of the form atom -> dt(...), where the nested dt structures follow the same pattern.

So if you want to find, for example, the choices for Indian cuisine, you can do

?- dt(food_type, Choices), member(indian->DT, Choices).
Choices = [(indian->dt(spicy, [(y->curry),  (n->curma)])),  (chinese->dt(fry, [(y->stirFry),  (n->chicken)])),  (malay->dt(chili, [(y->sambal),  (n->singgang)]))],
DT = dt(spicy, [(y->curry),  (n->curma)]) ;

And if you want to enumerate all the choices, you can do

?- dt(food_type, _Choices), member(Choice->DT, _Choices).
Choice = indian,
DT = dt(spicy, [(y->curry),  (n->curma)]) ;
Choice = chinese,
DT = dt(fry, [(y->stirFry),  (n->chicken)]) ;
Choice = malay,
DT = dt(chili, [(y->sambal),  (n->singgang)]).

(BTW: Actual results will be more noisy because they include the value of _Choices each time; I suppressed this by setting SWI-Prolog’s toplevel_print_anon flag to false, but that is irrelevant to the original question.)

It is important to note that the -> operator is not special here. It is not an “if”; in fact, any other binary operator or two-argument structure could have been used. It could have been written as blah(indian, [dt(...), ...]) and the structure would have essentially been the same (although less readable :-).

The trick is now to write a predicate that recurses into the dt structures, showing information and branching as needed… The StackOverflow answer already made a start with this.

Hope this helps a bit!

–Hans Nowak

1 Like

@zephyrfalcon, thank you so much for explaining it for a dummy like me :stuck_out_tongue: It really helped me tons for a project I am working on, and I really learned something. Here is how I am using it right now for those of you that might need something similar in the future.

dt('What is the food type',
    [indian  -> dt('Want it spicy?', [ y -> curry,   n -> curma ])
    ,chinese -> dt('Want it fried?',   [ y -> stirFry, n -> chicken ])
    ,malay   -> dt('Want chili?', [ y -> sambal,  n -> singgang ])
    ]).

interpreter(dt(About, Choices), Choice) :-
    % present a menu for choices
    % recurse on selected path
    About = Choice,
    write(About), nl,
    forall(member(Choice2->DT, Choices), (write(Choice2), nl)),
    read(Test),
    member(Test->DT, Choices),
    interpreter(DT, _).

% in case of error
interpreter(dt(About, Choices), dt(About, Choices)).

% when it reach a leaf, just unify
interpreter(Choice, Choice) :- 
    write(Choice), nl.

go :-
    dt(X, Y),
    interpreter(dt(X, Y), _).