Search code examples
prologprolog-setof

Find all facts with matching predicates


I have a factbase full of interacts relationships:

% Drug, Drug, Interaction Effect
interacts(terbinafine,tramadol,muscle_spasm).
interacts(terbinafine,triazolam,amnesia).
interacts(terbinafine,warfarin,arterial_pressure_nos_decreased).
interacts(terbinafine,warfarin,bradycardia).
interacts(terbinafine,rosiglitazone,hyperglycaemia).
interacts(terbinafine,allopurinol,arterial_pressure_nos_decreased).

And a function that determines if two drugs interact with each other:

interacts_with(D1, D2) :-
  interacts(D1, D2, _) ; interacts(D2, D1, _).

I am trying to list all of the interaction effects of two given drugs. Given that two drugs interact with each other (interacts_with() returns true), how would I go about collecting the side effects of those two drugs interacting?

For example:

interacts_with(terbinafine, warfarin).

Should return:

[arterial_pressure_nos_decreased, bradycardia]

I've been trying with findall/3, but am only getting what I pass in returned in a list containing more matches that exists:

?- findall([terbinafine, warfarin], interacts_with(D1, D2), Foo).
Foo = [[terbinafine, warfarin], [terbinafine, warfarin], [terbinafine, warfarin], [terbinafine, warfarin], [terbinafine, warfarin], [terbinafine, warfarin], [terbinafine, warfarin], [terbinafine|...], [...|...]|...].

Edit

I've attempted to implement what @false suggested:

    interacts_with(D1, D2) :-
        interacts(D1, D2, _) ; interacts(D2, D1, _).

    print_effects(D1, D2) :-
        ( interacts_with(D1,D2)
            -> findall([D1, D2], interacts_with(D1, D2), [])
            ; write(D1), write(" does not interact with "), write(D2)
        ).

And now I am getting:

?- print_effects(terbinafine, warfarin).
false.

Both setof and findall are returning false rather than the list of drugs that were passed in


Solution

  • First of all, your knowledge base consists of facts interacts/3: two drugs and their interaction. Then you define a predicate interacts_with/2 that has only two drugs as arguments. The interaction is hidden in the body of the rule and therefore not accessible from the head of the rule, which essentially is its interface. Looking again at interacts/3 you can query it interactively for the effects of two drugs combined:

       ?- interacts(terbinafine,warfarin,I).
    I = arterial_pressure_nos_decreased ? ;
    I = bradycardia
    

    Note that the result of the query is one of the predicates arguments (I) that is left as a variable. Prolog is then unifying this variable with all interactions that match both drugs. This is what I meant above when I wrote accessible from the head ... which essentially is its interface. You can use findall/3 or setof/3 to get all solutions for the above query at once as a list:

       ?- findall(I,interacts(terbinafine,warfarin,I),AI).
    AI = [arterial_pressure_nos_decreased,bradycardia]
    
       ?- setof(I,interacts(terbinafine,warfarin,I),AI).
    AI = [arterial_pressure_nos_decreased,bradycardia]
    

    The latter removes duplicates from the list if there are any.