次の実装は永久ループになります。
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にある場合は再試行します。それ以外の場合は、通常どおり繰り返します」と言っているようです。ここでの基本的な問題は、条件が常に成功するということです。
ゼロから書き直すよりも、あなたの問題に対するより直接的な解決策があると確信していますが、それは私にはやや単調なプロローグのように見えました。代わりに、「論理的に」より直接的に言える場合に備えて、私たちが言おうとしていることを再考しましょう。あなたが実際に言おうとしているのは、「特定のサイズのFromのランダムなサブセットを取得してください」です。元のコードの条件は、実際には「ねえ、私はすでにこの要素を見たので、先に進みましょう」の単なる箔ですが、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)
。varTo
はまだインスタンス化されていないため、To = [5|_G397]
最初に数値から5が選択された場合になります(デバッガーを使用してこれらの詳細を確認してください!)。
私が見ることができるあなたのコードを修正する最も簡単な方法は、アキュムレータを追加することです:
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)
) .
また、バックトラックでのループを回避するために「ガード」を追加しました