以下实现将永远循环。
pick_nums(0,_,_).
pick_nums(Count,From,To) :-
random_member(X,From),
( member(X,To) -> pick_nums(Count,From,To)
; C1 is Count-1,
pick_nums(C1,From,[X|To]) ) .
?- numlist(1,9,X), pick_nums(3,X,Y).
答案 0 :(得分:2)
这里的问题是pick_nums(Count, From, To)
内部pick_nums(Count, From, To)
的呼叫相同。这里的条件逻辑似乎是在说“给我一个随机的From元素,但是如果我已经把它放在To中再试一次;否则,通常会重复。”这里的基本问题只是你的状况总是成功。
我确信对你的问题有一个更直接的解决方案,而不是从头开始重写,但对我来说,它似乎有些单一的Prolog。相反,让我们重新思考一下我们想要说的话,以防我们能够更直接地“逻辑地”说出来。你实际上想说的是“让我得到一个具有一定大小的From of的随机子集。”原始代码中的条件实际上只是“嘿,我已经看过这个元素所以让我们继续前进”的陪衬“但是在Prolog中你经常说出你的意思,而不是告诉Prolog如何去做。
pick_nums(0, _, []).
pick_nums(Count, From, [X|SelectedFromRemaining]) :-
random_member(X, From),
select(X, From, Remaining),
C1 is Count - 1,
pick_nums(C1, Remaining, SelectedFromRemaining).
通过使用select/3
我能够生成没有所选元素的列表,然后我可以将其传递给下一个调用。这样我就不必担心我是否已经看过这个元素。请注意,这样做可能会导致性能下降,但我们用较少的语句来实现这一点,并且(希望)更清楚我们正在尝试做什么。
如果我们愿意进一步依赖SWI-Prolog,那么有一些内置库可以进一步简化这一过程。例如,random
库有一个谓词,它将为我们提供列表的随机排列。我们可以用以下方式清楚说明我们正在做的事情:
pick_nums(Count, From, To) :-
random_permutation(From, Scrambled),
append(To, _, Scrambled),
length(To, Count).
如果你总是希望将它与numlist
一起使用,那里还有另一个助手:
?- randset(3, 9, X).
X = [3, 4, 8].
?- randset(3, 9, X).
X = [2, 7, 9].
我希望这是足够的帮助。
答案 1 :(得分:1)
我认为问题是电话member(X,To)
。 var To
尚未实例化,如果首先从数字中选择5,它将变为To = [5|_G397]
(使用调试器查看这些详细信息!)。
我能看到的纠正代码的最简单方法是添加累加器:
pick_nums(Count, From, To) :-
pick_nums(Count, From, [], To).
pick_nums(0, _, Acc, Acc).
pick_nums(Count, From, Acc, To) :-
Count > 0, % avoid looping on backtracking
random_member(X, From),
( memberchk(X, Acc)
-> pick_nums(Count, From, Acc, To)
; C1 is Count-1,
pick_nums(C1, From, [X|Acc], To)
) .
我还添加了一名警卫'避免循环回溯