如何在Prolog中获得not效果
How to get the not effect in Prolog
我正在尝试创建一个递归调用自身的规则,并找到遍历有向图的所有可能路径。我正在使用 findall()
来这样做。函数是 traverse(Start,End)
.
我有:
traverse(Start,End,[li]) :-
node(Start,End),
append(End,Li).
traverse(Start,End,[Li]) :-
node(Start,Z),
traverse(Z,End),
append(Start,Li).
/*here I would like to check that Z is not a member of Li before
I call traverse(Z,End), I know I can check that it is a member
using member(Z,Li) but how to I check it is not a member in
prolog*/
findalltraverses(Start,End,[list]) :-
findall(_,traverse(Start,End).
开始之前:这种语言称为 prolog。
您的代码在不同级别有多个 mistakes/errors。这是它们的(不完整)列表:
findall
需要右括号。
findall
需要3个参数。第一个位置是您感兴趣的内容,第二个是如何获取数据(哪个调用),第三个是您的答案的容器。您对容器感兴趣,所以 findalltraverses
应该将其作为属性。
- 节点和边是有区别的。一条边连接两个节点。
- 变量以大写字母或下划线开头。
- 如果变量
L
与列表统一,您不必像 [L]
那样声明它,因为例如 L=[1,2,3]
[L]
会变成 [[1,2,3]]
这是一个包含 1
、2
和 3
. 列表的列表
- 递归调用经常有两种情况:结束设置和递归调用设置。你这个概念是对的。
append
有 3 个参数:lists L1
、L2
和 L3
,其中 L1
L2
附加的是 L3
.
- 您的谓词
traverse/3
有 3 个属性。没有为 2 个属性定义谓词 traverse/2
。
- 您不能覆盖变量。一旦设置了变量的值,就不能再更改它。例如:如果你有一个数字
N
并且想要增加它,你需要使用不同的变量来保存结果:N1 is N+1
.
- 将单个元素
E
添加到列表 L
作为头元素(意思是将其放在第一个位置)可以通过头尾写法来完成:[E|L]
。对于 E=1
和 L=[2,3]
,[E|L]
变为 [1,2,3]
。
- 为了避免无限循环,您需要跟踪您已经访问过的节点。但是,如果您对从
Start
到 End
的路径感兴趣,则您需要以某种方式转发该路径。不可能仅将列表与已访问节点一起使用,因为在通过递归返回时不会保留此知识。所以你的 traverse
谓词应该有 4 个属性。
- “不能”可以用
\+
表示,相当于“无法证明”
好的,这是截取的工作代码。递归结束部分有点不同,因为它不检查是否存在从 Start
到 End
的边,而是询问 Start
和 End
是否相同。如果是这样,则生成的路径仅包含此节点。这很重要,因为在代码中截断的路径将在通过递归(返回时构建([Start|Path]
),而包含访问节点的列表在时增长进入 递归 ([Start|Visited]
).
edge(a,b).
edge(b,c).
edge(c,d).
edge(a,d).
traverse(End,End,_,[End]).
traverse(Start,End,Visited,[Start|Path]) :-
edge(Start,Tmp),
\+ member(Tmp,Visited),
traverse(Tmp,End,[Start|Visited],Path).
findalltraverses(Start,End,Out):-
findall(List,traverse(Start,End,[],List),Out).
我们来测试一下!
?- findalltraverses(a,d,Out).
Out = [[a, b, c, d], [a, d]].
看起来不错。
我正在尝试创建一个递归调用自身的规则,并找到遍历有向图的所有可能路径。我正在使用 findall()
来这样做。函数是 traverse(Start,End)
.
我有:
traverse(Start,End,[li]) :-
node(Start,End),
append(End,Li).
traverse(Start,End,[Li]) :-
node(Start,Z),
traverse(Z,End),
append(Start,Li).
/*here I would like to check that Z is not a member of Li before
I call traverse(Z,End), I know I can check that it is a member
using member(Z,Li) but how to I check it is not a member in
prolog*/
findalltraverses(Start,End,[list]) :-
findall(_,traverse(Start,End).
开始之前:这种语言称为 prolog。
您的代码在不同级别有多个 mistakes/errors。这是它们的(不完整)列表:
findall
需要右括号。findall
需要3个参数。第一个位置是您感兴趣的内容,第二个是如何获取数据(哪个调用),第三个是您的答案的容器。您对容器感兴趣,所以findalltraverses
应该将其作为属性。- 节点和边是有区别的。一条边连接两个节点。
- 变量以大写字母或下划线开头。
- 如果变量
L
与列表统一,您不必像[L]
那样声明它,因为例如L=[1,2,3]
[L]
会变成[[1,2,3]]
这是一个包含1
、2
和3
. 列表的列表
- 递归调用经常有两种情况:结束设置和递归调用设置。你这个概念是对的。
append
有 3 个参数:listsL1
、L2
和L3
,其中L1
L2
附加的是L3
.- 您的谓词
traverse/3
有 3 个属性。没有为 2 个属性定义谓词traverse/2
。 - 您不能覆盖变量。一旦设置了变量的值,就不能再更改它。例如:如果你有一个数字
N
并且想要增加它,你需要使用不同的变量来保存结果:N1 is N+1
. - 将单个元素
E
添加到列表L
作为头元素(意思是将其放在第一个位置)可以通过头尾写法来完成:[E|L]
。对于E=1
和L=[2,3]
,[E|L]
变为[1,2,3]
。 - 为了避免无限循环,您需要跟踪您已经访问过的节点。但是,如果您对从
Start
到End
的路径感兴趣,则您需要以某种方式转发该路径。不可能仅将列表与已访问节点一起使用,因为在通过递归返回时不会保留此知识。所以你的traverse
谓词应该有 4 个属性。 - “不能”可以用
\+
表示,相当于“无法证明”
好的,这是截取的工作代码。递归结束部分有点不同,因为它不检查是否存在从 Start
到 End
的边,而是询问 Start
和 End
是否相同。如果是这样,则生成的路径仅包含此节点。这很重要,因为在代码中截断的路径将在通过递归(返回时构建([Start|Path]
),而包含访问节点的列表在时增长进入 递归 ([Start|Visited]
).
edge(a,b).
edge(b,c).
edge(c,d).
edge(a,d).
traverse(End,End,_,[End]).
traverse(Start,End,Visited,[Start|Path]) :-
edge(Start,Tmp),
\+ member(Tmp,Visited),
traverse(Tmp,End,[Start|Visited],Path).
findalltraverses(Start,End,Out):-
findall(List,traverse(Start,End,[],List),Out).
我们来测试一下!
?- findalltraverses(a,d,Out).
Out = [[a, b, c, d], [a, d]].
看起来不错。