Prolog - 约束库 - 变量范围
Prolog - Constraint Library - Variable Scope
我正在尝试用 Prolog 解决 LSAT 逻辑问题。我试图让 Prolog 告诉我一行中演员的可能值是否有效。
我写了以下内容:
:- use_module(library(clpfd)).
actor("Jeff",A) :-
A #>= 0,
A #<5.
actor("Rich",C) :-
C #>= 0,
C #<5,
actor("Jeff",A),
A #< C.
当我查询时:
?- actor("Rich",0).
false.
这是对的,因为 Jeff < 比 Rich 和 Jeff => 0 所以 Rich 不能是 0。
但是,当我查询时:
?- actor("Jeff",1), actor("Rich",1).
true
我也明白了,这是不可能的,因为 Rich > Jeff。
我觉得问题出在我的变量上。我不明白
actor("Jeff",A),
A #< C.
正在评估。在我看来,应该将 actor("Jeff",A) 设置为 actor("Jeff",1) 然后 1 #< 1 应该失败。
变量在一个子句或局部到该子句。
对于您的情况,要了解问题,请首先考虑以下查询:
?- actor("Rich", C),
C = 1.
C = 1.
这并没有告诉我们很多,所以我们应用以下纯粹的代数推理:对于目标 actor("Rich", C)
,我们替换单个匹配子句的 body :
?- C #>= 0,
C #< 5,
actor("Jeff",A),
A #< C,
C = 1.
答案是:
C = 1,
A = 0.
这表明当C
为1时,则A
为0
。但是,在顶层,您没有看到这一点,因为该变量仅出现在 within "Rich" 的子句中。明确表示有一个 解决方案 满足该子句中的约束,但它没有 link 我们想要的变量。
有几种出路。其中之一是使 A
作为 参数 可用,以便您可以从顶层明确引用它。一般来说,为了link相关实体在一起,你必须为你的子句每个引入参数,这样你就可以引用你需要推理的变量,而不是在每个子句中引入新的。
例如,在您的情况下,这可能如下所示:
actor("Jeff", A, _) :-
A #>= 0,
A #< 5.
actor("Rich", A, C) :-
C #>= 0,
C #< 5,
actor("Jeff", A, C),
A #< C.
我用 A
指代 Jeff,用 C
指代 Rich。
趁我们在的时候,让我们整理代码,并使用以下本质上等效版本:
actor(jeff, A, _) :- A in 0..4.
actor(rich, A, C) :- C in 0..4, actor(jeff, A, C), A #< C.
确保您理解以下答案:
?- actor(jeff, 1, C), actor(rich, C, 1).
C = 0.
您的原始示例现在产生 false
,完全符合预期:
?- actor(rich, 1, 1).
false.
因此,原则上应该可以解决你的任务。
但是,有一种更简单的方法可以解决所有这些问题,它可以避免具体化过载。
与其煞费苦心地跟踪名称和相应变量之间的联系,不如让我们直接使用带有预期名称的变量。比如这个你怎么说:
?- Jeff in 0..4,
Rich in 0..4,
Jeff #< Rich.
这使用Prolog 变量来表示人,并使工作变得更加简单。在此表示中,您的查询变为:
?- Jeff in 0..4,
Rich in 0..4,
Jeff #< Rich,
Jeff = 1,
Rich = 1.
这显然会导致 false
。
我正在尝试用 Prolog 解决 LSAT 逻辑问题。我试图让 Prolog 告诉我一行中演员的可能值是否有效。
我写了以下内容:
:- use_module(library(clpfd)).
actor("Jeff",A) :-
A #>= 0,
A #<5.
actor("Rich",C) :-
C #>= 0,
C #<5,
actor("Jeff",A),
A #< C.
当我查询时:
?- actor("Rich",0).
false.
这是对的,因为 Jeff < 比 Rich 和 Jeff => 0 所以 Rich 不能是 0。
但是,当我查询时:
?- actor("Jeff",1), actor("Rich",1).
true
我也明白了,这是不可能的,因为 Rich > Jeff。
我觉得问题出在我的变量上。我不明白
actor("Jeff",A),
A #< C.
正在评估。在我看来,应该将 actor("Jeff",A) 设置为 actor("Jeff",1) 然后 1 #< 1 应该失败。
变量在一个子句或局部到该子句。
对于您的情况,要了解问题,请首先考虑以下查询:
?- actor("Rich", C), C = 1. C = 1.
这并没有告诉我们很多,所以我们应用以下纯粹的代数推理:对于目标 actor("Rich", C)
,我们替换单个匹配子句的 body :
?- C #>= 0, C #< 5, actor("Jeff",A), A #< C, C = 1.
答案是:
C = 1, A = 0.
这表明当C
为1时,则A
为0
。但是,在顶层,您没有看到这一点,因为该变量仅出现在 within "Rich" 的子句中。明确表示有一个 解决方案 满足该子句中的约束,但它没有 link 我们想要的变量。
有几种出路。其中之一是使 A
作为 参数 可用,以便您可以从顶层明确引用它。一般来说,为了link相关实体在一起,你必须为你的子句每个引入参数,这样你就可以引用你需要推理的变量,而不是在每个子句中引入新的。
例如,在您的情况下,这可能如下所示:
actor("Jeff", A, _) :- A #>= 0, A #< 5. actor("Rich", A, C) :- C #>= 0, C #< 5, actor("Jeff", A, C), A #< C.
我用 A
指代 Jeff,用 C
指代 Rich。
趁我们在的时候,让我们整理代码,并使用以下本质上等效版本:
actor(jeff, A, _) :- A in 0..4. actor(rich, A, C) :- C in 0..4, actor(jeff, A, C), A #< C.
确保您理解以下答案:
?- actor(jeff, 1, C), actor(rich, C, 1). C = 0.
您的原始示例现在产生 false
,完全符合预期:
?- actor(rich, 1, 1). false.
因此,原则上应该可以解决你的任务。
但是,有一种更简单的方法可以解决所有这些问题,它可以避免具体化过载。
与其煞费苦心地跟踪名称和相应变量之间的联系,不如让我们直接使用带有预期名称的变量。比如这个你怎么说:
?- Jeff in 0..4, Rich in 0..4, Jeff #< Rich.
这使用Prolog 变量来表示人,并使工作变得更加简单。在此表示中,您的查询变为:
?- Jeff in 0..4, Rich in 0..4, Jeff #< Rich, Jeff = 1, Rich = 1.
这显然会导致 false
。