Prolog - Using terms to represent and access complex, nested data
data-structures, nested, prolog
Solution
The short answer to your questions would be:
monitor(X) :-
my_computer([_, monitor(X), _, _]).
And similarly for `keyboard` or `mouse`, etc. The `motherboard` would be a layer deeper:
motherboard(X) :-
my_computer([case([motherboard(X), _, _, _, _), _, _, _]).
These predicates of course assume a fixed structure. If you wanted it a bit more general, you could do a more elaborate "hunt" for the embedded functors (`monitor`, `motherboard`, etc).
Depending upon your broader application goals, it's not clear to me that this is the best representation of the data. Good enough for now, but context might want to take it in a different direction.
Here's another approach, thinking of the data as individual facts which imply a tree relationship. Basically just `has` relationships. Separate the "material" facts as `material(Item, Type)`:
item(my_computer, case).
item(my_computer, monitor).
item(my_computer, keyboard).
item(my_computer, mouse).
item(case, motherboard).
item(case, power_supply_unit).
item(case, central_processing_unit).
item(case, random_access_memory).
item(case, graphics_processing_unit).
item(motherboard, board).
item(motherboard, ports).
item(motherboard, slots).
item(motherboard, capacitors).
item(power_supply_unit, casing).
item(power_supply_unit, cable).
item(power_supply_unit, connectors).
item(power_supply_unit, capacitors).
item(power_supply_unit, fan).
item(power_supply_unit, transformer).
item(central_processing_unit, board).
item(central_processing_unit, fan).
item(central_processing_unit, heatsink).
item(random_access_memory, board).
item(graphics_processing_unit, board).
item(graphics_processing_unit, ports).
item(graphics_processing_unit, capacitors).
item(graphics_processing_unit, fan).
item(graphics_processing_unit, heatsink).
item(monitor, lcd_screen).
item(monitor, inverter).
item(monitor, frame).
item(keyboard, key).
item(keyboard, frame).
item(keyboard, cable).
item(mouse, key).
item(mouse, wheel).
item(mouse, casing).
item(mouse, cable).
material(board, plastic).
material(slots, plastic).
material(capacitors, plastic).
material(ports, metal).
material(casing, metal).
material(cable, plastic).
material(connectors, plastic).
material(fan, plastic).
material(heatsink, metal).
material(lcd_screen, plastic).
material(inverter, plastic).
material(frame, plastic).
material(key, plastic).
material(cable, plastic).
Then you can define a predicate to generate the tree for whatever level you wish. Here's an example that does it in the form of terms (not lists):
structure(Item, Structure) :-
( item(Item, _)
-> findall(T, (item(Item, R), structure(R, T)), Rs),
Structure =.. [Item |Rs]
; Structure = Item
).
So then:
:- structure(case, S).
S = case(motherboard(board,ports,slots,capacitors),
power_supply_unit(casing,cable,connectors,capacitors,fan,transformer),
central_processing_unit(board,fan,heatsink),
random_access_memory(board),
graphics_processing_unit(board,ports,capacitors,fan,heatsink)
)
This could easily be changed to provide results in a list form instead. For example, here's a predicate that takes the above facts and gives the form you originally presented in your question:
structure(Item, Tree) :-
( item(Item, _)
-> findall(T, (item(Item, R), structure(R, T)), Rs),
Tree =.. [Item, Rs]
; material(Item, Material),
Tree =.. [Item, Material]
).
And the `item` becomes a trivial result for a `where_used(Item, Parent)` predicate:
where_used(Item, Parent) :-
item(Parent, Item).
Again, it all depends upon how you want to use and manage the data.
Problem
``` my_computer([ case([ motherboard([board(plastic),ports(metal),slots(plastic),capacitors(plastic)]), power_supply_unit([casing(metal),cables(plastic),connectors(plastic),capacitors(plastic),fan(plastic),transformer(metal)]), central_processing_unit([board(plastic),fan(plastic),heatsink(metal)]), random_access_memory([board(plastic)]), graphics_processing_unit([board(plastic),ports(metal),capacitors(plastic),fan(plastic),heatsink(metal)]) ]), monitor([ lcd_screen(plastic),inverter(plastic),frame(plastic) ]), keyboard([ key(plastic),frame(plastic),cable(plastic) ]), mouse([ key(plastic),wheel(plastic),casing(plastic),cable(plastic) ]) ]). ``` What should I do to in order to run questions like `monitor(X).` or `motherboard(X)` to give one or all layers of (sub)materials (Like `my_computer(X).` would do) ? Would the code below be more useful for asking such questions? Question about one layer of submaterials is answered easily this way. ``` my_computer([case,monitor,keyboard,mouse]). case([motherboard,power_supply_unit,central_processing_unit,random_access_memory,graphics_processing_unit]). motherboard([board,ports,slots,capacitors]). power_supply_unit([casing,cables,connectors,capacitors,fan,transformer]). central_processing_unit([board,fan,heatsink]). random_access_memory([board]). graphics_processing_unit([board,ports,capacitors,fan,heatsink]). monitor([lcd_screen,inverter,frame]). keyboard(keys,frame,cable). mouse([keys,wheel,casing,cable]). ```