问题 将列表理解翻译成Prolog
我在Haskell中有一个列表理解,我想翻译成Prolog。
列表理解的重点是旋转4乘4格:
rotate :: [Int] -> [Int]
rotate grid = [ grid !! (a + 4 * b) | a <- [0..3], b <- [0..3] ]
现在在Prolog中,我翻译成这样:
rotateGrid([T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15],
[T0,T4,T8,T12,T1,T5,T9,T13,T2,T6,T10,T14,T3,T7,T11,T15]).
我们可以做得更好吗?
9943
2018-04-12 19:35
起源
答案:
我们可以用 findall/3
列表推导(参见 SWI-Prolog文档)。例如。,
?- findall(X, between(1,10,X), Xs).
Xs = [1,2,3,4,5,6,7,8,9,10]
Xs
是一个包含所有可以统一的值的列表 X
什么时候 X
是1到10之间的数字。这大致相当于Haskell表达式 let Xs = [x | x <- [1..10]]
(1)。你可以看一个 findall/3
声明:“查找[第一参数]的所有值,使[第二参数中的条件]成立,并将这些值放在列表中,[第三参数]”。
我用过 findall/3
写一个谓词 rotate_grid(+Grid, ?RotatedGrid)
。这是我在谓词中使用的近似Haskell-Prolog等价的列表;每一行显示Haskell表达式将评估的值与具有相同值的Prolog变量之间的关系:
a <- [0..3]
= A
在 between(0, 3, A)
b <- [0..3]
= B
在 between(0, 3, B)
(a + 4 * d)
= X
在 X is A + 4 * D
<Grid> !! <Index>
= Element
在 nth0(Index, Grid, Element)
然后我们只需要找到所有的值 Element
:
rotate_grid(Grid, RotatedGrid) :-
findall( Element,
( between(0,3,A),
between(0,3,B),
Index is A + 4 * B,
nth0(Index, Grid, Element) ),
RotatedGrid
).
为了验证这会产生正确的转换,我从问题中下载了Prolog代码并提出以下查询:
?- rotate_grid([t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15],
[t0,t4,t8,t12,t1,t5,t9,t13,t2,t6,t10,t14,t3,t7,t11,t15]).
| true.
脚注:
(1): between/3
实际上并不是类似的 [m..n]
,因为后者返回一个值列表 m
至 n
哪里 between(M,N,X)
将在回溯时使用M和N之间的每个值(包括)实例化X.要获取SWI-Prolog中的数字列表,我们可以使用 numlist(M,N,Ns)
。所以更严格的类比 x <- [1.10]
将是结合 member(X, Ns), numlist(1, 10, Ns)
。
10
2018-04-12 20:47
你想要一个列表的排列。不考虑具体要素。因此,您可以将Haskell签名推广到
rotate :: [x] -> [x]
对于Prolog来说,这已经是一个非常有价值的提示:不会考虑列表的元素 - 甚至不会比较元素。所以Prolog解决方案应该能够直接处理变量,如下所示:
| ? - rotateGrid(L,R)。
L = [_ A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P],
R = [_ A,_E,_I,_M,_B,_F,_J,_N,_C,_G,_K,_O,_D,_H,_L,_P]。
而您的原始定义完美地处理了这一点
使用列表推导的版本表明自己可以通过回溯来实现,必须采取某些预防措施。运用 findall/3
,正如@aBathologist所建议的那样,将重命名变量:
| ? - 长度(L,16),rotate_grid(L,R)。
L = [_ A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P],
R = [_ Q,_ R,_S,_T,_U,_V,_W,_X,_Y,_Z,_A1,_B1,_C1,_D1,_E1,_F1]。
内置谓词 bagof/3
解决了这个问题。请注意,我们必须明确声明所有本地存在变量:
rotate_grid2(Grid,RotatedGrid): -
bagof(
元件,
A ^ B ^ Index ^%存在变量的声明
((0,3,A)之间,
之间(0,3,B),
指数是A + 4 * B,
nth0(索引,网格,元素)
)
RotatedGrid)。
对于短于16个元素的列表,Haskell版本会产生干净的错误,但在这里我们得到相当随机的结果:
| ? - L = [1,2,3,4],rotate_grid(L,R)。
L = [1,2,3,4],
R = [1,2,3,4]?
是
| ? - L = [1,2,3,4,5],rotate_grid(L,R)。
L = [1,2,3,4,5],
R = [1,5,2,3,4]?
是
这是由于枚举和“生成”具体元素的部分之间的分离不明确。最干净的方法是添加 length(Grid, 16)
在目标之前 bagof/3
。
Prolog中的列表理解
目前,只有B-Prolog提供了一种列表推导形式:
R @ = [E:A在0..3中,B在0..3中,[E,I],(I是A + 4 * B,nth0(I,L,E))]。
但是,它没有解决第二个问题:
| ? - L = [1,2,3],R @ = [E:A在0..3,B在0..3,[E,I],(I是A + 4 * B,nth0(I ,L,E))。
L = [1,2,3]
R = [1,2,3]
是
4
2018-04-13 10:19
使用循环谓词foreach / 4
如果理解应该保留变量,这在约束编程中是重要的,那么Prolog系统可以提供谓词foreach / 4。这个谓词是foreach / 2的DCG伙伴。
以下是通过findall / 3保留变量的方法
结果R包含根据ISO的新变量
findall / 3的核心语义:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.1)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- functor(L,foo,5), findall(X,
(between(1,5,N), M is 6-N, arg(M,L,X)), R).
L = foo(_5140, _5142, _5144, _5146, _5148),
R = [_5210, _5204, _5198, _5192, _5186].
以下是如何通过foreach / 4保留变量,
结果列表与化合物具有相同的变量
我们开始:
Jekejeke Prolog 3, Runtime Library 1.3.0
(c) 1985-2018, XLOG Technologies GmbH, Switzerland
?- [user].
helper(N,L) --> [X], {M is 6-N, arg(M,L,X)}.
Yes
?- functor(L,foo,5), foreach(between(1,5,N),helper(N,L),R,[]).
L = foo(_A,_G,_M,_S,_Y),
R = [_Y,_S,_M,_G,_A]
使用foreach / 4而不是bagof / 3可能看起来有点过头了。 foreach / 4可能只会在实现Picat循环时显示其全部潜力,因为它可以构建约束,bagof / 3无法做到。
foreach / 4是一个没有完全实现所有解决方案的实现,然后被回溯。它与bagof / 3共享变量的重建,但仍允许在闭包的结合中进行回溯。
2
2017-08-20 14:17