通过corecursion解决Prolog中的动态编程



我想通过Prolog中的corecursion来解决以下动态规划问题。但是我坚持进行广度优先搜索,我想以相应的方式实现:

有一栋n层的建筑,有电梯只能 一次上2层,一次下3层。用 动态编程编写一个函数来计算 电梯从楼层到达的台阶数 I 至 J 楼。

我已经决定了懒惰列表表示形式。惰性列表只是 Prolog 闭包 C,可以调用它来产生一个头和尾部的新闭包。

示例一个流:

one(1, one).

然后,可以简单地将 Haskell take 谓词编码如下:

take(0, _, L) :- !, L = [].
take(N, C, [X|L]) :- N > 0, 
call(C, X, D), 
M is N-1, 
take(M, D, L). 

下面是一个运行示例:

?- take(5, one, X).
X = [1, 1, 1, 1, 1].
?- take(10, one, X).
X = [1, 1, 1, 1, 1, 1, 1, 1, 1|...].

在这个共递归Prolog解决方案中,我们需要两个构建块。

一个构建块是一种在 Prolog 中共同递归枚举搜索树的方法。我们采用的想法是,Prolog关闭期限应该带有一个议程,其中包含路径,因此应该扩展节点。然后,我们可以从一个只包含根的议程开始:

% tree(-Path, -LazyPaths)
tree(H, T) :-
tree([[]], H, T). 

为了存档广度第一个枚举,我们将附加新的扩展路径,从而在议程的末尾附加节点。这可以通过一个简单的列表追加谓词调用来完成,以便缺少的定义如下所示。在完整的二叉树路径中,因此节点总是扩展两次:

% tree(+Paths, -Path, -LazyPaths)
tree([X|Y], X, tree(Z)) :-
append(Y, [[0|X],[1|X]], Z). 

下面是一个运行示例:

?- take(5, tree, L).
L = [[],[0],[1],[0,0],[1,0]]
?- take(10, tree, L).
L = [[],[0],[1],[0,0],[1,0],[0,1],[1,1],[0,0,0],[1,0,0],[0,1,0]] 

在评估器问题的情况下,我们将有一个路径,因此节点扩展并不总是导致两个继任者。如果我们在k层,电梯可以去k+2或k-3层,前提是电梯留在建筑物内。因此,我们读到了一个共递归谓词步骤,它确实模拟了电梯的所有可能路径:

?- take(5, steps(7,[[2]]), L).
L = [[2],[4,2],[1,4,2],[6,4,2],[3,1,4,2]]
?- take(10, steps(7,[[2]]), L).
L = [[2],[4,2],[1,4,2],[6,4,2],[3,1,4,2],[3,6,4,2],
[5,3,1,4,2],[5,3,6,4,2],[2,5,3,1,4,2],[7,5,3,1,4,2]]

最后一个障碍和第二个构建块是在Prolog中获得Haskell掉落。我们的目标不是为布尔条件采用 Prolog 闭包项参数的谓词,而是只提供一个枚举惰性列表元素的谓词,并且谓词的用户可以在 Prolog 延续中过滤。

% drop_while(+LazyList, -Element)
drop_while(C, P) :-
call(C, Q, T),
(P = Q; drop_while(T, P)).

如果我们把所有东西放在一起,我们会得到一个共同回避的Prolog解决方案,除了以广度一阶计算结果之外,它甚至可以通过回溯枚举评估器问题的所有无限解决方案:

?- elevator(7,2,6,L), length(L,N).
L = [6,4,2],
N = 3 ;
L = [6,4,2,5,3,1,4,2],
N = 8 ;
L = [6,4,7,5,3,1,4,2],
N = 8 

最新更新