The following implementation will loop forever.
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).
The problem here is that inside
pick_nums(Count, From, To)you have an identical call ofpick_nums(Count, From, To). The conditional logic here seems to be saying “get me a random element of From, but try again if I already have it in To; otherwise, recur normally.” The basic problem here is simply that your condition always succeeds.I’m sure there’s a more direct solution to your problem than rewriting from scratch, but it seemed like somewhat unidiomatic Prolog to me. Instead, let’s rethink what we’re trying to say in case we can say it “logically” more directly. What you’re actually trying to say is “get me a random subset of From of with a certain size.” The conditional in your original code is really just a foil for “hey, I’ve already seen this element so let’s move on” but frequently in Prolog you get further by saying what you mean than telling Prolog how to do it.
By using
select/3I’m able to produce the list without the selected element in it, which I can then pass on to the next call. This way I don’t have to worry about whether or not I’ve seen this element already. Note that there could be a performance penalty associated with doing it this way, but we got there in fewer statements and it (hopefully) is more clear what we’re trying to do.If we’re willing to depend further on SWI-Prolog there are some built-in libraries that can simplify this process even further. For instance, the
randomlibrary has a predicate that will give us a random permutation of a list. We can state what we’re doing pretty clearly using that:If you always expect to use it with
numlistthere’s another helper in there that does this already:I hope this is enough help.