Prolog - 使用术语表示和访问复杂的嵌套数据

时间:2015-02-24 16:05:29

标签: data-structures prolog nested

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

我应该怎么做才能运行monitor(X).motherboard(X)等问题来提供一个或所有(子)材料层(如my_computer(X).那样)?

以下代码对于提出此类问题会更​​有用吗?关于一层子材料的问题很容易以这种方式回答。

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

3 个答案:

答案 0 :(得分:2)

对你的问题的简短回答是:

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

同样适用于keyboardmouse等。motherboard会更深层次:

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

这些谓词当然假设一个固定的结构。如果你想要它更通用一些,你可以对嵌入式仿函数(monitormotherboard等)进行更精细的“搜索”。

根据您更广泛的应用目标,我不清楚这是数据的最佳表示。现在已经够好了,但是上下文可能想要把它带到另一个方向。

<小时/> 这是另一种方法,将数据视为暗示树关系的个别事实。基本上只是has关系。将“重要”事实分开为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).

然后,您可以定义谓词以生成您希望的任何级别的树。这是一个以术语(不是列表)的形式来做的例子:

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

那么:

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

这可以很容易地更改为以列表形式提供结果。例如,这是一个谓词,它采用上述事实并提供您最初在问题中提出的表单:

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

item成为where_used(Item, Parent)谓词的一个简单结果:

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

同样,这完全取决于您希望如何使用和管理数据。

答案 1 :(得分:2)

修改

为了好玩,我拼凑了一对规则,这些规则概括了对name([attr_1([attr_1_1,...attr_1_n([...])]), ..., attr_N([...]))形式结构的任意深度访问;即,存储为与my_computer/1具有相同通用形式的事实的数据。如果你愿意,你可以使用它。无论如何,这是Prolog强有力的高阶谓词+明确评价+同质性的一个很好的小证明:

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

假设我们有my_computer/1这样的事实(正如问题的第一部分所示),谓词attribute/2可用于访问任何属性,嵌套在任何深度,像这样:

?- 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 

我选择->/2运算符作为访问者路径只是因为它具有方便的关联性(即右:(a -> (b -> c))),它有点暗示“进入一系列嵌套术语”。但它可能不是一个很好的选择。如果我经常使用它,我会为此目的找到一个好的运算符并用op/3声明它。


由于my_computer/1中的所有术语都在列表中,因此您可以使用成员创建一般访问谓词:

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

可以像这样使用:

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

当然,由于monitor嵌套在case中,因此您需要采用更具体的方法来访问前者。

根据数据的详细程度,这可能是pairsassociation listsrecords的一个很好的用例。 (还有新的SWI Prolog扩展提供dict化合物,但我不推荐它。我发现它们比它们值得更麻烦。但这可能只是我身边的一个缺点。 )

第二个代码块的缩进使得my_computer/1事实与其他代码之间存在层次关系,但这是一种幻觉。适当的缩进表明每个都被简单地说成是一个独立的事实:

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

因此,只有在给定命名空间中只有一台计算机具有相关部分时,此表示才有效。否则,如果您有第二台计算机,您将无法分辨哪个mouse(X)的呼叫与哪台计算机相关联。但是,您可以为每台计算机命名,然后将不同的属性定义为计算机名称和术语列表之间的关系。那你的代表就是这样:

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

可以像这样查询此代码:

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

(一旦你掌握了Prolog,你可能想要调查Logtalk,这是Prolog的OOP扩展。我从未使用它,但听起来很有趣。)

答案 2 :(得分:1)

您还可以使用明确的子句语法规则(DCG)定义物料清单(BOM)。例如:

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

具有组件的零件,例如monitor,表示为非终端。不是由其他部件制成的零件,例如keys表示为终端(即方括号之间)。标准phrase/2谓词可用于获取特定部分的物料清单。例如:

| ?- 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

如果您需要表示特定部分的详细信息,例如风扇有三种不同的模式,其中一种具有自己的RPM,您可以将部件名称(原子)解释为Prolog模块或Logtalk对象标识符。然后,模块或对象可以很好地保存有关零件的详细信息。