2015-02-24 9 views
6
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) 
    ]) 
]). 

Cosa devo fare per poter eseguire domande come monitor(X). o motherboard(X) per dare uno o tutti gli strati della (sotto) materiali (come my_computer(X). farebbe)?Prolog - utilizzando termini per rappresentare e complessi accesso, dati nidificate

Il seguente codice sarebbe più utile per porre domande del genere? La domanda su uno strato di submateriali viene fornita facilmente in questo modo.

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]). 
+1

Non deve esserci uno spazio davanti alla parentesi rotonda di apertura e la parola precedente (più volte). Quindi scrivi 'mouse (' ecc. – false

+0

@false ora per darmi questo X = [caso ([scheda madre ([scheda (plastica), porte (metallo), slot (plastica), condensatori (plastica)]), power_supply_unit ([ involucro (di metallo), cavi (di plastica), connettori (di plastica), condensatori (...) | ...]), unità_produttiva_ centrale ([scheda (plastica), ventola (di plastica), dissipatore di calore (...)]), random_access_memory ([board (plastic)]), graphics_processing_unit ([board (...) | ...])]), monitor ([lcd_screen (plastica), inverter (plastica), cornice (plastica)]), tastiera ([tasti (plastica), cornice (plastica), cavo (plastica)]), mouse ([tasti (plastica), ruota (plastica), involucro (plastica), cavo (...)])]. – Crownless

+2

Perfetto! esattamente quello che stai descrivendo.Se vuoi accedere ai dettagli, ti consiglio di prendere prima un libro Prolog, come Art of Prolog e di imparare prima la lingua .. – false

risposta

2

La risposta breve alle vostre domande potrebbe essere:

monitor(X) :- 
    my_computer([_, monitor(X), _, _]). 

E allo stesso modo per keyboard o mouse, ecc motherboard sarebbe uno strato più profondo:

motherboard(X) :- 
    my_computer([case([motherboard(X), _, _, _, _), _, _, _]). 

Questi predicati Ovviamente assumiamo una struttura fissa. Se lo volevi un po 'più generale, potresti eseguire una "caccia" più elaborata per i functors incorporati (monitor, motherboard, ecc.).

A seconda degli obiettivi di applicazione più ampi, non è chiaro per me che questa sia la migliore rappresentazione dei dati. Abbastanza buono per ora, ma il contesto potrebbe voler prenderlo in una direzione diversa.


Ecco un altro approccio, pensando ai dati come singoli fatti che implicano una relazione tra alberi. Fondamentalmente solo le relazioni has. Separare i fatti "materiali" come 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). 

Quindi è possibile definire un predicato per generare l'albero per qualsiasi livello lo si desidera. Ecco un esempio che lo fa in forma di termini (non liste):

structure(Item, Structure) :- 
    ( item(Item, _) 
    -> findall(T, (item(Item, R), structure(R, T)), Rs), 
     Structure =.. [Item |Rs] 
    ; Structure = Item 
    ). 

Dunque:

:- 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) 
     ) 

Questo potrebbe essere facilmente modificato per fornire risultati in una forma di elenco invece. Ad esempio, ecco un predicato che prende i fatti di cui sopra e dà la forma originariamente presentato nella tua domanda:

structure(Item, Tree) :- 
    ( item(Item, _) 
    -> findall(T, (item(Item, R), structure(R, T)), Rs), 
     Tree =.. [Item, Rs] 
    ; material(Item, Material), 
     Tree =.. [Item, Material] 
    ). 

E il item diventa un risultato banale per un where_used(Item, Parent) predicato:

where_used(Item, Parent) :- 
    item(Parent, Item). 

Anche in questo caso, tutto dipende da come si desidera utilizzare e gestire i dati.

+1

Mi piace vedere come proliferano le possibilità! Hai iniziato con un approccio super semplice e completamente adeguato che risolveva il problema in un unico passaggio. Ho proposto un approccio leggermente più generale. Quindi il tuo commento mi ha fatto riflettere un po 'più profondamente sul mio approccio. Quando ho finito di aggiungere la mia * maggiore * generalizzazione, mirata a immergermi nel singolo oggetto dati composto, era venuto fuori con questa * adorabile * analisi orientata al database! Mi sembra che entrambi gli approcci abbiano vantaggi e svantaggi distinti e, insieme, mostrano la bella flessibilità di Prolog :) –

+0

(non intendevo "maggiore" nel senso di "maggiore del tuo approccio" ma maggiore di quello che Inizialmente avevo proposto di usare 'member/2' up). –

+1

@aBathologist non preoccuparti. Non l'ho preso negativamente. :) – lurker

2

Edit:

Per divertimento, ho messo insieme un paio di regole che generalizzano l'accesso profondità arbitraria alle strutture della forma name([attr_1([attr_1_1,...attr_1_n([...])]), ..., attr_N([...])); Ad esempio, ai dati archiviati come fatti che hanno la stessa forma generale di my_computer/1. Puoi usarlo, se vuoi.In ogni caso, si tratta di un bel po 'di dimostrazione della potente ricetta di Prolog dei predicati di ordine superiore + valutazione esplicita + homoiconicity:

attribute(Thing, Path) :- 
    call(Thing, Attributes), 
    path_through_attributes(Path, Attributes). 

path_through_attributes(Path, Attributes) :- 
    (Path = (Func -> NextPath), atom(Func) 
    -> 
     Attr =.. [Func, NextAttributes], 
     member(Attr, Attributes), 
     path_through_attributes(NextPath, NextAttributes) 
    ; 
     compound(Path), 
     member(Path, Attributes) 
    ). 

Supponendo che abbiamo un fatto come my_computer/1 (proprio come appare nella prima parte della tua domanda), il predicato attribute/2 può essere utilizzato per accedere a qualsiasi dei suoi attributi, annidati a qualsiasi profondità, in questo modo:

?- attribute(my_computer, case(X)). 
X = [motherboard([board(plastic), ports(metal), slots(plastic), capacitors(plastic)]), power_supply_unit([casing(metal), cables(plastic), connectors(plastic), capacitors(plastic), fan(plastic), transformer(...)]), central_processing_unit([board(plastic), fan(plastic), heatsink(metal)]), random_access_memory([board(plastic)]), graphics_processing_unit([board(plastic), ports(metal), capacitors(...)|...])] 

?- attribute(my_computer, case -> power_supply_unit(X)). 
X = [casing(metal), cables(plastic), connectors(plastic), capacitors(plastic), fan(plastic), transformer(metal)] 

?- attribute(my_computer, case -> power_supply_unit -> transformer(X)). 
X = metal 

ho scelto l'operatore ->/2 per la funzione di accesso percorso semplicemente perché ha una comoda associatività (ie , a destra: (a -> (b -> c))) ed è un po 'suggestivo e di "raggiungere una serie di termini annidati". Ma probabilmente non è una grande scelta. Se avessi intenzione di usarlo frequentemente, sarei venuto con un buon operatore per lo scopo e lo dichiarerei con op/3.


Dal momento che tutti i termini in my_computer/1 sono in una lista, si potrebbe fare un predicato di accesso generale usando membro:

computer_attribute(Attr) :- 
    my_computer(Attributes), 
    member(Attr, Attributes). 

Esso può essere utilizzato in questo modo:

?- computer_attribute(monitor(X)). 
X = [lcd_screen(plastic), inverter(plastic), frame(plastic)] 

Di Ovviamente, poiché monitor è nidificato in case, è necessario disporre di un approccio più specifico per accedere al primo.

A seconda dell'elaborazione dei dati, questo potrebbe essere un buon uso di pairs, association lists o records. (C'è anche la nuova estensione Prolog SWI che fornisce il composto dict, ma io non lo consiglio, ho trovato che sono più problemi che valgono, ma potrebbe essere solo un difetto al mio fianco.)

Il rientro del secondo blocco di codice fa sembrare che esista una relazione gerarchica tra il fattore my_computer/1 e gli altri, ma questa è un'illusione. Una corretta indentazione chiarisce che ciascuno è semplicemente dichiarato come fatto autonomo:

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]). 

Di conseguenza, questa rappresentazione funziona solo se si dispone di un solo computer con le parti interessate in un dato spazio dei nomi. In caso contrario, se si dispone di un secondo computer, non sarà possibile stabilire quale chiamata a mouse(X) deve essere associata a quale computer. Tuttavia, è possibile assegnare a ciascun computer un nome e quindi definire i diversi attributi come relazioni tra il nome del computer e un elenco di termini. Allora la vostra rappresentazione vorrebbe così:

computer(mine). 
case(mine, [motherboard,power_supply_unit,central_processing_unit,random_access_memory,graphics_processing_unit]). 
.... 

Questo codice può essere interrogato in questo modo:

?- computer(Name), motherboard(Name, Specs). 
Name = mine, 
Specs = [board, ports, slots, capacitors]. 

(Una volta che si dispone di una solida conoscenza di Prolog, si potrebbe desiderare di indagare Logtalk, che è un OOP estensione per Prolog. Non l'ho mai usato, ma sembra divertente.)

+1

L'uso di 'member' è sicuramente la strada da percorrere poiché è organizzato con elenchi (+1). È lì che mi sono diretto, ma mi hai battuto. :) – lurker

+0

Grazie per questo, e non sottrarre punti per il mio errore di ortografia persistente di query come cava: P. Io (+1) ti ho dedicato alla concisione e alla semplice efficacia della tua risposta originale. Ma vedo che hai anche aggiornato il tuo con un approccio più elaborato! –

1

È anche possibile definire una distinta materiali (BOM) utilizzando regole grammaticali definite clausole (DCG). Ad esempio:

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

Parti con componenti, ad es. monitor, sono rappresentati come non-terminali.Parti che non sono fatte di altre parti, ad es. keys, sono rappresentati come terminali (ad esempio tra parentesi quadre). Il predicato standard phrase/2 può essere utilizzato per ottenere l'elenco dei materiali per una parte specifica. Ad esempio:

| ?- phrase(monitor, Parts). 

Parts = [lcd_screen,inverter,frame] 
yes 

| ?- phrase(case, Parts). 

Parts = [board,ports,slots,capacitors,casing,cables,connectors,capacitors,fan,transformer,board,fan,heatsink,board,board,ports,capacitors,fan,heatsink] 
yes 

Se è necessario rappresentare informazioni dettagliate su una parte specifica, ad es. che una ventola ha tre diverse modalità, una con i suoi RPM, è possibile interpretare il nome della parte (che è un atomo) come un modulo Prolog o un identificatore di oggetto Logtalk. Il modulo o l'oggetto possono quindi contenere bene i dettagli della parte.