Get random predicate from knowledge base. Prolog
For example I have:
upred(mary, have, knife).
upred(john, have, sword).
upred(sam, have, bowl).
upred(sword, is, long).
How can I get random predicate?
% call this and get random predicate as Pred
get_random_pred(Pred) :开发者_运维知识库-
Funny, this is something I've been worrying about lately as well. I have a partial solution which depends on the dynamic store and identifying the facts you want to be able to get at randomly. I'm not in love with it because it does depend on the dynamic store and also because it depends on the fabrication of metadata, which can get unsynched. However, it may be sufficient for your purposes. It also doesn't perfectly capture your API, because you have to offer some clue what "kind" of fact you're interested in. In practice, it would probably work out OK though, because you're unlikely to find yourself in a situation where any fact would do, because it'll probably fail at the next pattern match.
My basic trick is to use =..
to disassemble predicates, and use asserta
to assign each fact an index value. If you wanted this to perform better you'd have to use some indexing directive to tell Prolog you want it to index all the way to the third field of my random_fact
, but I didn't take this that far. For small databases (not WordNet) this will probably be OK, but for larger ones you'll probably need the performance.
% random_fact(Head, Instantiation, Index)
:- dynamic(random_fact/3).
% fact_count(Head, Count)
:- dynamic(fact_count/2).
% one big side-effect to make it possible to query for a random predicate
prepare_randomization_metadata(Goal) :-
findall(Goal, Goal, Occurrances),
prepare_randomization_metadata(Occurrances, 0),
Goal =.. [Head|_],
length(Occurrances, N),
asserta(fact_count(Head, N)).
prepare_randomization_metadata([], _).
prepare_randomization_metadata([Goal|Goals], N) :-
Goal =.. [Head|_],
asserta(random_fact(Head, Goal, N)),
N1 is N+1,
prepare_randomization_metadata(Goals, N1), !.
So as you can see, the engine here is to basically take a given goal and build up a little metadata database. It could probably be improved by someone who knows Prolog better than me. To use it, you drive it like so:
?- prepare_randomization_metadata(upred(X, Y, Z)).
true.
Now you have a database with facts in it like this:
random_fact(upred, upred(mary, have, knife), 0).
random_fact(upred, upred(john, have, sword), 1).
...
This is something you can reason over with Prolog. So if you want the second predicate, you could query it like so:
?- random_fact(upred, X, 1)
X = upred(mary, have, knife) ;
false.
Now get_random_pred
is pretty easy, but we need an additional argument to identify the "kind" of fact we want:
get_random_pred(Head, Pred) :-
fact_count(Head, N),
% pick a random number between 0 and the # of facts we have for this pred
random(0, N, I),
random_fact(Head, Pred, I), !.
I am not good enough yet with Prolog to tell you why this sometimes thought it had multiple solutions for me, but it did, so I inserted the red cut at the end. But, if you'd like multiple solutions, it's easy enough to write a version that does that too.
endless_random_facts(Head, Fact) :- repeat, get_random_pred(Head, Fact).
For example:
?- get_random_pred(upred, X).
X = upred(sword, is, long) ;
X = upred(john, have, sword) ;
X = upred(mary, have, knife) ;
X = upred(john, have, sword) ;
X = upred(john, have, sword) ;
Anyway, I hope this helps, despite the deficiencies. I've only tested in SWI-Prolog.
SWI-Prolog has nth_clause/3, so I guess a simple solution would be:
?- I is random(4)+1, nth_clause(upred(_,_,_), I, R), clause(H, B, R).
I = 1,
R = <clause>(0000018B7AECE610),
H = upred(mary, have, knife),
B = true.
?- I is random(4)+1, nth_clause(upred(_,_,_), I, R), clause(H, B, R).
I = 2,
R = <clause>(0000018B7AECC690),
H = upred(john, have, sword),
B = true.
You could also use the predicate property number_of_clauses/1.
精彩评论