以下實現將永久循環。如何從列表中隨機選擇元素?
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).
以下實現將永久循環。如何從列表中隨機選擇元素?
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).
這裏的問題是,裏面pick_nums(Count, From, To)
你有pick_nums(Count, From, To)
相同的電話。這裏的條件邏輯似乎在說:「給我一個From的隨機元素,但如果我已經在To中有它,請再試一次;否則,正常重現。」這裏的基本問題只是你的病情總是成功。
我確信有一個更直接的解決方案來解決您的問題,而不是從頭開始重寫,但它對我來說似乎有點單一的Prolog。相反,讓我們重新考慮我們想要說的話,以便我們可以更直接地說「邏輯」。你實際上想說的是「給我一個隨機的子集,從一定的尺寸。」原始代碼中的條件實際上僅僅是「嘿,我已經看到了這個元素,所以讓我們繼續前進」的陪襯,但是在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].
我希望這是足夠的幫助。
我覺得問題是電話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)
) .
我還添加了一個「警衛」,以避免循環的回溯
完美的答案,謝謝! –