我认为除了JB的推荐之外,还有一些其他的方法可以解决。一个俗气的技巧是ProductTypeN
通过将其添加到查询的末尾来强制执行排序:
ProductType1 @> ProductType2,
ProductType2 @> ProductType3.
这将为您提供三种产品类型的一种排列,消除您所看到的组合爆炸。
一种更复杂的技术是用于setof/3
枚举解决方案。因为它将所有答案作为一个集合生成,所以它必须对值进行排序,这以与俗气的技巧基本相同的方式删除重复项。
more_than_two_product_types(Manufacturer) :-
setof(Manufacturer, T^manufacturer(T,Manufacturer), Manufacturers),
member(Manufacturer, Manufacturers),
setof(Type, Thing^(manufacturer(Thing, Manufacturer), store(Thing, Type)), [_,_,_|_]).
这很复杂,所以让我们分解一下。首先,此条件生成制造商列表:
setof(Manufacturer, T^manufacturer(T,Manufacturer), Manufacturers),
元谓词采用setof/3
构造函数表达式、模板表达式并返回结果列表。这个将收集所有解决方案manufacturer(T, Manufacturer)
并将它们收集到一个列表中。我们只对制造商的名称感兴趣,所以模板参数只是Manufacturer
. T^
语法告诉setof/3
它是T
一个自由变量manufacturer(T, Manufacturer)
,所以我们不关心它实例化到什么。这是必不可少的,否则setof/3
它会为每种类型生成一个解决方案,这不是我们想要的。
此行迭代了新的制造商列表:
member(Manufacturer, Manufacturers),
这条复杂的生产线为我们找到了制造商生产的所有类型的产品:
setof(Type, Thing^(manufacturer(Thing, Manufacturer), store(Thing, Type)), [_,_,_|_]).
这里的目标表达式是序列(manufacturer(Thing, Manufacturer), store(Thing, Type))
。这就是说,找到Thing
这个制造商制造的,然后找到Type
那个东西。同样,Thing^
语法表明我们并不真正关心事物是什么,因此Type
一次获得所有解决方案。模板没有将其绑定到列表以供我们处理,而是[_,_,_|_]
与任何包含至少三个项目的列表统一。我们并不真正关心这些项目是什么,所以它们都是空白的。在您的控制台中进行测试,看看它与什么相结合:
?- [1,2,3] = [_,_,_|_].
true.
?- [1,2] = [_,_,_|_].
false.
?- [1,2,3,4] = [_,_,_|_].
true.
这将产生至少三个解决方案,然后将它们丢弃以获得成功,或者如果它产生的更少则失败。
如您所见,使用 Prolog 给猫剥皮的方法不止一种。:)