Prolog findall Implementation

耗尽温柔 提交于 2019-11-27 09:42:44

Besides asserting data as you go, you can also use an extra-logical predicate such as nb_setarg/3. Then once a parent is found, you fail back past nb_setarg and find another parent. All previously found solutions should stay in the term you did nb_setarg on, then after all results are exhausted, the nb_setarg term is the answer. The SWI-Prolog example is good, but its just a counter. Try doing it with a list (or better yet: difference list) that builds as you go.

Davorin Ruševljan

Take a look at this solution. Note that this solution uses dynamic predicate named queue in order to cache all solutions until all possibilities are exhausted. Once no more solution exists, implementation retracts all facts and composes the list.

This is of course a bit simplified solution, imagine what would happen if two findall would be active at the same time. It is also a bit fragile on exact semantics of assert and retract if particular prolog implementation

user978383

Thanks for you help everyone. I managed to sovle it in the end by adding a predicate which checked each item against the current list, and failed if it was already present:

find(X, Loa) :- find(X, [], Loa), !.
find(X, Acc, Loa) :- dec(Y, X), uList(Y, Acc, AccNew), find(X, AccNew, Loa).
find(_, Acc, Acc).

dec(X,Y) :- parent(X,Y).
dec(X,Y) :- parent(X,Z), dec(Z,Y).

uList(X, [], [X])  :- !.
uList(H, [H|_], _) :- !, fail.
uList(X, [H|T], L) :- uList(X, T, Rtn), L = [H|Rtn].

Here is a solution that uses SWI-Prolog queues and threads. It uses the old existing API and does something along Tarau's Engines. I assume that the thread creation will copy the template and the goal. And then I assume that the queue send will again do a copy of each solution.

So compared to the classical findall you will have a surplus on one template and goal copy, but otherwise it will also copy each solution as the classical findall. Source on gist here. But by modding threadall2, which does the collection, one could also implement all kinds of aggregates:

% threadall(+Term, +Goal, -List)
threadall(T, G, L) :-
   message_queue_create(J, [max_size(1)]),
   thread_create(threadall3(T, G, J), _, [detached(true)]),
   thread_get_message(J, A),
   threadall2(J, A, L),
   message_queue_destroy(J).

% threadall3(+Term, +Goal, +Queue)
threadall3(T, G, J) :-
   G, thread_send_message(J, the(T)), fail.
threadall3(_, _, J) :-
   thread_send_message(J, no).

% threadall2(+Queue, +Term, -List)
threadall2(J, the(T), [T|L]) :- !,
   thread_get_message(J, A),
   threadall2(J, A, L).
threadall2(_, no, []).

Here is an example run. I hope I did the bookkeeping correctly. The thread was created with detach(true), so we do not need some handle when the thread terminates. The message queue is explicitly destroyed. Here are some example runs in SWI-Prolog, we see that it works as expected:

Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.23)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam

?- threadall(X, between(0, 5, X), L).
L = [0, 1, 2, 3, 4, 5].

?- threadall(X-Y, (between(0, 2, X), 
                   threadall(Z, between(0, 2, Z), Y)), L).
L = [0-[0, 1, 2], 1-[0, 1, 2], 2-[0, 1, 2]].

Our code only implements the usual happy path: We only implement the the/1 and no/0 message. Further since we dont use setup_call_cleanup/3 it is also not safe to use the solution with interrupts. Also the last argument is not steadfast. This is all left as an exercise for the reader to implement these additional requirements and corresponding alternate paths.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!