Reputation: 23876
This are facts:
address(host1, '10.0.0.1').
address(host2, '10.0.0.2').
And I can query a fact:
?- address(host1, X).
X = '10.0.0.1'.
I am new to Prolog but I am used to relational databases. In a SQL database I put constrains on the data to prevent errors. If I want to ensure, that all addresses are in the class A network 10.0.0.0/8, I can put a constrain on the table column:
create table hosts (
name text,
address text check (address like '10.%')
);
How would I do the same in Prolog to prevent wrong facts?
Upvotes: 0
Views: 305
Reputation: 40768
The facts you have written are equivalent to the following rules:
address(host1, '10.0.0.1') :- true. address(host2, '10.0.0.2') :- true.
true/0
is the most general goal: It always succeeds.
To constrain the clauses more strongly, you can add further goals. In Prolog, each goal acts as a constraint on answers.
@coder's answer is spot on: You can simply write rules to add these checks, and they are similar to (and more expressive than) your database constraints.
For example, you can write:
host_ip(host1, IP) :- IP = '10.0.0.1', good_ip(IP). host_ip(host2, IP) :- IP = '10.0.0.2', good_ip(IP).
Note the predicate naming convention: To make clear what each argument is, I am using host_ip/2
instead of simply address/2
.
A possible definition of good_ip/1
would be:
good_ip(IP) :- atom_concat('10.', _, IP).
This is true iff IP
starts with 10.
.
At this point, the question arises whether you have chosen the most suitable representation of IP addresses!
If you frequently need to analyze components, consider for example the representation:
ip(A,B,C,D)
In this representation, we could write:
host_ip(host1, IP) :- IP = ip(10,0,0,1), good_ip(IP). host_ip(host2, IP) :- IP = ip(10,0,0,2), good_ip(IP). good_ip(ip(10,_,_,_)).
EDIT: As you correctly point out in the comments, this only prevents fetching wrong IPs, not asserting or adding wrong facts or clauses.
To make the latter possible, consider the approach outlined by @lurker: You can simply define a rule like:
add_host_ip(Host, IP) :- good_ip(IP), assertz(host_ip(Host,IP)).
This prevents you from adding wrong IPs to the database, assuming you consistently use this interface predicate.
Note also that you combine this with throwing an exception if a wrong IP is given. For example:
good_ip(ip(First,_,_,_)) :- dif(First, 10), throw(wrong_subnet(First)). good_ip(ip(10,_,_,_)).
Example session:
?- add_host_ip(host3, ip(11,0,0,0)). ERROR: Unhandled exception: wrong_subnet(11) ?- add_host_ip(host4, ip(10,0,0,1)). true. ?- host_ip(host3, IP). false. ?- host_ip(host4, IP). IP = ip(10, 0, 0, 1).
This illustrates that invalid IPs are now neither stored nor returned as answers.
Upvotes: 6
Reputation: 12982
You could write:
find_address(X,Y):-
address(X,Y),
atom_chars(Y,Y1),
consecutive_member('1','0',Y1).
consecutive_member(X,Y,[X,Y|_]).
consecutive_member(X,Y,[H1,H2|T]):-
dif(H1,1),dif(H2,0),
consecutive_member(X,Y,T).
And query find_address(host1,X). which succeeds only if there is an address for the host1 starting with '10':
?- find_address(host1,X).
X = '10.0.0.1' ;
false.
Upvotes: 1