我正在生成随机坐标并添加到我的列表中,但首先我需要验证该坐标是否已存在。我正在尝试使用member
,但在我调试时,我看到它无效:
我的代码基本上是这样的:
% L is a list and Q is a count that define the number of coordinate
% X and Y are the coordinate members
% check if the coordniate already exists
% if exists, R is 0 and if not, R is 1
createCoordinates(L,Q) :-
random(1,10,X),
random(1,10,Y),
convertNumber(X,Z),
checkCoordinate([Z,Y],L,R),
(R is 0 -> print('member'), createCoordinates(L,Q); print('not member'),createCoordinates(L,Q-1).
checkCoordinate(C,L,R) :-
(member(C,L) -> R is 0; R is 1).
% transforms the number N in a letter L
convertNumber(N,L) :-
N is 1, L = 'A';
N is 2, L = 'B';
...
N is 10, L = 'J'.
%call createCoordinates
createCoordinates(L,20).
当我调试时,这是输出:
在这张图片中,我处于第一次交互,L是空的,因此R应为1但始终为0,坐标始终是列表的一部分。
我的印象是member
子句在我的列表中添加坐标并且没有意义
答案 0 :(得分:3)
首先,我建议将您的问题分解成更小的部分。你应该有一个制作随机坐标的程序:
random_coordinate([X,Y]) :-
random(1, 10, XN), convertNumber(XN, X),
random(1, 10, Y).
其次,你的checkCoordinate/3
正在将Prolog的成功/失败转换为整数,这对于Prolog来说只是忙碌的工作,并没有真正为你改善生活。 memberchk/2
完全足以完成您的任务(member/2
也可以工作,但比必要的更强大)。这里真正的问题不是member/2
不起作用,而是你试图在出路时建立这个列表参数,但你需要它存在于检查它的路上。
我们通常在Prolog中通过添加第三个参数并在路径中将值前置到列表中来解决此类问题。然后基本情况将该列表与出站列表等同,我们用较低的arity程序保护整个事物。换句话说,我们这样做:
random_coordinates(N, Coordinates) :- random_coordinates(N, [], Coordinates).
random_coordinates(0, Result, Result).
random_coordinates(N, CoordinatesSoFar, FinalResult) :- ...
现在我们有两件事,memberchk/2
应该按照我们需要的方式工作:
random_coordinates(N, CoordinatesSoFar, FinalResult) :-
N > 0, succ(N0, N), % count down, will need for recursive call
random_coordinate(Coord),
(memberchk(Coord, CoordinatesSoFar) ->
random_coordinates(N, CoordinatesSoFar, FinalResult)
;
random_coordinates(N0, [Coord|CoordinatesSoFar], FinalResult)
).
这似乎做了我们想要的事情:
?- random_coordinates(10, L), write(L), nl.
[[G,7],[G,3],[H,9],[H,8],[A,4],[G,1],[I,9],[H,6],[E,5],[G,8]]
?- random_coordinates(10, L), write(L), nl.
[[F,1],[I,8],[H,4],[I,1],[D,3],[I,6],[E,9],[D,1],[C,5],[F,8]]
最后,我注意到您继续使用以下语法:N is 1, ...
。我告诫你,这对我来说似乎是一个错误,因为这与N = 1
之间没有区别,你的谓词可能只是有点令人厌烦:
convertNumber(1, 'A').
convertNumber(2, 'B').
...
我倾向于用char_code/2
进行计算,但这种结构实际上可能更好。
另一个暗示你做错了的事情是参数L
到createCoordinates/2
在所有情况下都会传递,而不会在其中任何一个中进行检查。在Prolog中,我们经常有变量似乎只是无意义地传递,但它们通常会改变位置或被多次使用,如random_coordinates(0, Result, Result)
;虽然那里似乎没有发生任何事情,但实际发生的是管道:构建参数成为结果值。直接在变量中没有任何有趣的事情发生,但它正在被探测。但是你的代码中没有任何东西发生在L上,除了它被认为是一个新的坐标。但是你实际上从来没有给它添加任何东西,所以没有理由期望任何东西会在L中出现。
编辑请注意,@ lambda.xy.x通过在子句的开头添加新坐标并仅在正文中的递归调用之后检查列表来解决他们的答案中的问题,从而避免需要第二个列表参数。
编辑2 另请查看@ lambda.xy.x的其他解决方案,因为当N接近100时,它具有更好的时间复杂度。
答案 1 :(得分:2)
由于我已经编写过,所以这是一个替代解决方案:构建基块为gen_coord_notin/2
,可以保证在排除列表C
方面有一个全新的解决方案Excl
。
gen_coord_notin(C, Excl) :-
random(1,10,X),
random(1,10,Y),
( memberchk(X-Y, Excl) ->
gen_coord_notin(C, Excl)
;
C = X-Y
).
诀窍在于,如果新结果,我们只会将C
与新结果统一起来。
然后我们只需要将代数折叠成N
次迭代:
gen_coords([], 0).
gen_coords([X|Xs], N) :-
N > 0,
M is N - 1,
gen_coords(Xs, M),
gen_coord_notin(X, Xs).
备注1 :由于坐标始终是2元组,因此列表表示会产生不需要的错误(例如,写入[X | Y]而不是[X,Y])。传统上,像-
这样的中缀运算符用于分隔元组,但与使用coord(X,Y)
没有任何区别。
备注2 :此谓词本质上是非逻辑的(即,两次调用gen_coords(X, 20)
将导致X
的不同替换。您可以使用元级谓词var/1
,nonvar/1
,ground/1
,integer
等来防范非gen_coord(1-2, [1-1])
等非感性调用。
备注3 :条件没有多个解决方案(比较成员(X,[A,B])和memberchk(X,[A,B]))也很重要。一般来说,这可以通过调用once/1
来实现,但我在这里使用了一个专门的谓词memberchk/2
。
答案 2 :(得分:2)
我刚刚意识到我的其他解决方案的性能非常糟糕,N接近100。原因是随着可能的坐标减少,生成和测试方法将花费更长时间。有一种替代解决方案可以生成所有坐标并选择N个随机坐标:
all_pairs(Ls) :-
findall(X-Y, (between(1,10,X), between(1,10,Y)), Ls).
remove_index(X,[X|Xs],Xs,0).
remove_index(I,[X|Xs],[X|Rest],N) :-
N > 0,
M is N - 1,
remove_index(I,Xs,Rest,M).
n_from_pool(_Pool, [], 0).
n_from_pool(Pool, [C|Cs], N) :-
N > 0,
M is N - 1,
length(Pool, L),
random(0,L,R),
remove_index(C,Pool,NPool,R),
n_from_pool(NPool, Cs, M).
gen_coords2(Xs, N) :-
all_pairs(Pool),
n_from_pool(Pool, Xs, N).
现在查询
?- gen_coords2(Xs, 100).
Xs = [4-6, 5-6, 5-8, 9-6, 3-1, 1-3, 9-4, 6-1, ... - ...|...] ;
false.
按预期成功。错误消息
? - gen_coords2(Xs,101)。
错误:随机/ 1:域错误:not_less_than_one' expected, found
0'
当我们尝试生成比可能更多的不同元素时,不是很好,但比非终止更好。