Prolog: Rules with nothing but anonymous variables

2019-08-11 14:37发布

问题:

Prolog's grammar uses a <head> :- <body> format for rules as such:

tree(G) :- acyclic(G) , connected(G). , denoting status of G as a tree depends on status as acyclic and connected.

This grammar can be extended in an implicit fashion to facts. Following the same example:

connected(graphA) suggests connected(graphA):-true.

In this sense, one might loosely define Prolog facts as Prolog rules that are always true.

My question: Is in any context a bodiless rule (one that is presumed to be true under all conditions) ever appropriate? Syntactically such a rule would look as follows.

graph(X). (suggesting graph(X):-true.)

回答1:

Before answering, to rephrase your question:

In Prolog, would you ever write a rule with nothing but anonymous variables in the head, and no body?

The terminology is kind of important here. Facts are simply rules that have only a head and no body (which is why your question is a bit confusing). Anonymous variables are variables that you explicitly tell the compiler to ignore in the context of a predicate clause (a predicate clause is the syntactical scope of a variable). If you did try to give this predicate clause to the Prolog compiler:

foo(Bar).

you will get a "singleton variable" warning. Instead, you can write

foo(_).

and this tells the compiler that this argument is ignored on purpose, and no variable binding should be attempted with it.

Operationally, what happens when Prolog tries to prove a rule?

  • First, unification of all arguments in the head of the rule, which might lead to new variable bindings;
  • Then, it tries to prove the body of the rule using all existing variable bindings.

As you can see, the second step makes this a recursively defined algorithm: proving the body of a rule means proving each rule in it.

To come to your question: what is the operational meaning of this:

foo(_).

There is a predicate foo/1, and it is true for any argument, because there are no variable bindings to be done in the head, and always, because no subgoals need to be proven.

I have seen at least one use of such a rule: look at the very bottom of this section of the SWI-Prolog manual. The small code example goes like this:

term_expansion(my_class(_), Clauses) :-
        findall(my_class(C),
                string_code(_, "~!@#$", C),
                Clauses).

my_class(_).

You should read the linked documentation to see the motivation for doing this. The purpose of the code itself is to add at compile time a table of facts to the Prolog database. This is done by term expansion, a mechanism for code transformations, usually used through term_expansion/2. You need the definition of my_class/1 so that term_expansion/2 can pick it up, transform it, and replace it with the expanded code. I strongly suggest you take the snipped above, put it in a file, consult it and use listing/1 to see what is the effect. I get:

?- listing(my_class).
my_class(126).
my_class(33).
my_class(64).
my_class(35).
my_class(36).

true.

NB: In this example, you could replace the two occurrences of my_class(_) with anything. You could have just as well written:

term_expansion(foobar, Clauses) :-
        findall(my_class(C),
                string_code(_, "~!@#$", C),
                Clauses).

foobar.

The end result is identical, because the operational meaning is identical. However, using my_class(_) is self-documenting, and makes the intention of the code more obvious, at least to an experienced Prolog developer as the author of SWI-Prolog ;).



回答2:

A fact is just a bodiless rule, as you call it. And yes, there are plenty of use cases for bodiless facts:

  1. representing static data
  2. base cases for recursion
  3. instead of some curly brace language pseudo code

    boolean is_three(integer x) {
        if (x == 3) { return  true; }
        else        { return false; }
    }
    

    we can simply write

    is_three(3).
    


回答3:

This is often how the base case of a recursive definition is expressed.



回答4:

To highlight what I was initially looking for, I'll include the following short answer for those who might find themselves asking my initial question in the future.

An example of a bodiless rule is, as @Anniepoo suggested, a base case for a recursive definition. Look to the example of a predicate, member(X,L) for illustration:

member(X,[X|T]). /* recursive base case*/
member(X,[H|T]):- member(X,T).

Here, the first entry of the member rule represents a terminating base case-- the item of interest X matching to the head of the remaining list.

I suggest visiting @Boris's answer (accepted) for a more complete treatment.



标签: prolog