有许多可能的方法可以用约束对此类依赖项进行建模。我在这篇文章中考虑了 CLP(FD) 和 CLP(B) 约束,因为它们最常用于解决组合任务。
首先考虑 CLP(FD),它在许多方面使用更频繁,也更方便。当使用 CLP(FD) 约束时,您再次有几个选项来表示您的任务。但是,无论您最终选择哪种模型,您都必须首先将表示中的所有项目切换到约束求解器实际上可以推理的合适实体。对于 CLP(FD),这意味着将您的实体切换为 整数。
将您的实体转换为相应的整数非常简单,这也是为什么 CLP(FD) 约束也足以对实际上不包含整数但可以映射到整数的域上的任务进行建模的原因之一。所以,让我们假设您不是在推理 f1、f2 和 f3 的特性,而是关于 整数 0、1 和 2 或任何其他集合适合您的整数。
您可以直接将您的需求转换到这个新域。例如,而不是:
[f1,s1] 表示:f1 需要s1
我们可以说例如:
0 -> 3 表示:0 需要3
这使我们已经非常接近 CLP(FD) 约束,使我们能够对整个问题进行建模。我们只需要再进行一次精神上的飞跃,即可获得一种表示,使我们能够对所有需求进行建模。我们现在使用 CLP(FD) 变量 来指示是否必须满足特定要求才能获得所需的特征,而不是 具体 整数。我们将使用变量R1, R2, R3, ... 来表示需要哪些要求,对每个可能的要求使用0(不需要)或1(需要) .
此时,您必须针对您真正想要描述的内容建立一个清晰的心智模型。我解释一下我的想法:我想描述三件事之间的关系:
-
功能列表
Fs
- 功能和需求之间的依赖关系列表
Ds
- 列表
Rs 要求
我们已经考虑过如何表示所有这些实体:(1) 是一个 整数 列表,表示我们想要获得的特征。 (2) 是 F -> R 对的列表,表示“功能 F 需要需求 R”,(3) 是布尔变量列表,指示最终是否需要每个需求。
现在让我们尝试将所有这些实体相互关联。
首先要做的事情:如果不需要任何功能,这一切都是微不足道的:
features_dependencies_requirements([], _, _).
但是,如果确实需要某个功能怎么办?嗯,很简单:我们只需要考虑该功能的依赖关系:
features_dependencies_requirements([F|Fs], Ds, Rs) :-
member(F->R, Ds),
所以我们在R 中有功能F 的要求。现在我们只需要在Rs 中找到合适的变量来表示需求R。但是我们如何找到正确的变量呢?毕竟,Prolog 变量“没有领结”,或者——对于外国人来说——缺少一个我们可以将其与其他变量区分开来的标记。因此,在这一点上,我们实际上会发现能够很好地从Rs 中挑选出一个变量,给定它的需求名称。因此,假设我们将Rs 表示为I=R 形式的pairs 列表,其中I 是定义要求的整数,R 是布尔指标表示是否需要该要求。鉴于这种表示,我们可以将上面的整个子句定义如下:
features_dependencies_requirements([F|Fs], Ds, Rs) :-
member(F->I, Ds),
member(I=1, Rs),
features_dependencies_requirements(Fs, Ds, Rs).
就是这样。这完全关联了一系列特性、依赖项和需求,以便第三个参数指示获得特性所必需的需求。
此时,细心的读者将会看到,在上面的代码中,没有任何 CLP(FD) 约束,事实上,将特征转换为整数是完全没有必要的。我们也可以使用原子来表示特征和要求,使用上面显示的完全相同的代码。
示例查询和答案:
?- features_dependencies_requirements([f3,f1],
[f1->s1,f2->s2,f3->s3,f3->s4],
[s1=S1,s2=S2,s3=S3,s4=S4])。
S1 = S3, S3 = 1 ;
S1 = S4, S4 = 1 ;
错误的。
显然,我做了以下假设:依赖关系是分离的,这意味着如果至少满足一个的需求,则可以实现该功能。如果你想把它变成一个连词,你显然必须改变它。您可以首先将依赖关系表示为F -> [R1,R2,...R_n]。
除此之外,将实体转换为整数是否仍然有用? 是的,因为您的许多约束可能也可以使用 CLP(FD) 约束来制定,并且您需要 整数 才能使其工作。
为了帮助您入门,以下两种方法可能适用于您的情况:
- 使用约束reification来表达什么暗示什么。例如:
F #==> R。
- 使用诸如
table/2 之类的全局约束来表达关系。
特别是在第一种情况下,CLP(B) 约束也可能有用。您始终可以使用布尔变量来表示是否必须满足要求。