Related
In many functional programming languages, it is possible to "redefine" local variables using a let expression:
let example =
let a = 1 in
let a = a+1 in
a + 1
I couldn't find a built-in Prolog predicate for this purpose, so I tried to define a let expression in this way:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
replace(Subterm0, Subterm, Term0, Term) :-
( Term0 == Subterm0 -> Term = Subterm
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist(replace(Subterm0,Subterm), Args0, Args),
Term =.. [F|Args]
).
let(A,B) :-
((D,D1) = (A1 is B1,C is B1);
(D,D1) = (A1=B1,C=B1)),
subsumes_term(D,A),
D=A,
replace(A1,C,B,B2),
call((D1,B2)).
main :- let(A = 1,(
writeln(A),
let(A is A+1,(
writeln(A),
let(A is A * 2,(
writeln(A)
))
))
)).
This implementation appears to incorrect, since some of the variables are bound before being replaced. I want to define an expression that would allow more than one variable to be "redefined" simultaneously:
main :- let((A = 1, B = 2), % this will not work with the let/2 predicate that I defined
let((A=B,B=A),(
writeln(A),
writeln(B)
))
).
Is it possible to implement a let expression in a way that allows several variables to be redefined at the same time?
The issue with defining let as a normal predicate is that you can't redefine variables that appear outside the outermost let. Here is my attempt at a more correct version, which uses goal expansion. (To me it makes sense, because as far as I know, in lisp-like languages, let cannot be defined as a function but it could be defined as a macro.)
%goal_expansion(let(Decl,OriginalGoal),Goal) :- %% SWI syntax
goal_expansion(let(Decl,OriginalGoal), _M, _, Goal, []) :- %%SICStus syntax
!,
expand_let(Decl,OriginalGoal,Goal).
expand_let(X, OriginalGoal, Goal) :-
var(X),
!,
replace(X,_Y,OriginalGoal,NewGoal),
Goal=(true,NewGoal).
expand_let(X is Decl, OriginalGoal, Goal) :-
var(X),
!,
replace(X,Y,OriginalGoal,NewGoal),
Goal=(Y is Decl,NewGoal).
expand_let(X = Decl, OriginalGoal, Goal) :-
var(X),
!,
replace(X,Y,OriginalGoal,NewGoal),
Goal=(Y = Decl,NewGoal).
expand_let([],OriginalGoal, Goal) :-
!,
Goal=OriginalGoal.
expand_let([L|Ls],OriginalGoal, Goal) :-
!,
expand_let_list([L|Ls],OriginalGoal,InitGoals,NewGoal),
Goal=(InitGoals,NewGoal).
expand_let((L,Ls),OriginalGoal, Goal) :-
!,
expand_let(Ls,OriginalGoal, SecondGoal),
expand_let(L,SecondGoal, Goal).
expand_let_list([],Goal,true,Goal).
expand_let_list([L|Ls],OriginalGoal,(Init,InitGoals),NewGoal):-
(
var(L)
->
replace(L,_,OriginalGoal,SecondGoal),
Init=true
;
L=(X=Decl)
->
replace(X,Y,OriginalGoal,SecondGoal),
Init=(Y=Decl)
;
L=(X is Decl)
->
replace(X,Y,OriginalGoal,SecondGoal),
Init=(Y is Decl)
),
expand_let_list(Ls,SecondGoal,InitGoals,NewGoal).
This is reusing the replace/4 predicate defined in the question. Note also that the hook predicate differs between Prolog versions. I am using SICStus, which defines goal_expansion/5. I had a quick look at the documentation and it seems that SWI-Prolog has a goal_expansion/2.
I introduced a different syntax for multiple declarations in a single let: let((X1,X2),...) defines X1, then defines X2 (so is equivalent to let(X1,let(X2,...))), while let([X1,X2],...) defines X1 and X2 at the same time (allowing the swap example).
Here are a few example calls:
test1 :- let(A = 1,(
print(A),nl,
let(A is A+1,(
print(A),nl,
let(A is A + 1,(
print(A),nl
))
))
)).
test2 :- A=2,let([A=B,B=A],(print(B),nl)).
test3 :- A=1, let((
A is A * 2,
A is A * 2,
A is A * 2
),(
print(A),nl
)),print(A),nl.
test4 :- let([A=1,B=2],let([A=B,B=A],(print(A-B),nl))).
test5 :- let((
[A=1,B=2],
[A=B,B=A]
),(
print(A-B),nl
)).
let is essentially a way of creating (inline to the source) a new, local context in which to evaluate functions (see also: In what programming language did “let” first appear?)
Prolog does not have "local contexts" - the only context is the clause. Variables names are only valid for a clause, and are fully visible inside the clause. Prolog is, unlike functional programs, very "flat".
Consider the main:
main :- let(A = 1,(
writeln(A),
let(A is A+1,(
writeln(A),
let(A is A * 2,(
writeln(A)
))
))
)).
Context being clauses, this is essentially "wrong pseudo code" for the following:
main :- f(1).
f(A) :- writeln(A), B is A+1, g(B).
g(A) :- writeln(A), B is A*2, h(B).
h(A) :- writeln(A).
?- main.
1
2
4
true.
The let doesn't really bring much to the table here. It seems to allow one to avoid having to manually relabel variables "on the right" of the is, but that's not worth it.
(Now, if there was a way of creating nested contexts of predicates to organize code I would gladly embrace that!).
Let's probe further for fun (and because I'm currently trying to implement the Monad Idiom to see whether that makes sense).
You could consider creating an explicit representation of the context of variable bindings, as if you were writing a LISP interpreter. This can be done easily with SWI-Prolog dicts, which are just immutable maps as used in functional programming. Now note that the value of a variable may become "more precise" as computation goes on, as long as it has any part that is still a "hole", which leads to the possibility of old, deep contexts getting modified by a current operation, not sure how to think about that.
First define the predicate to generate a new dict from an existing one, i.e. define the new context from the old one, then the code becomes:
inc_a(Din,Din.put(a,X)) :- X is Din.a + 1.
twice_a(Din,Din.put(a,X)) :- X is Din.a * 2.
main :- f(_{a:1}).
f(D) :- writeln(D.a), inc_a(D,D2), g(D2).
g(D) :- writeln(D.a), twice_a(D,D2), h(D2).
h(D) :- writeln(D.a).
The A has gone inside the dict D which is weaved through the calls.
You can now write a predicate that takes a dict and the name of a
context-modifying predicate ModOp, does something that depends on the context (like calling writeln/1 with the value of a), then modifies the
context according to ModOp.
And then deploy foldl/4 working over a list, not of objects, but of operations, or rather, names of operations:
inc_a(Din,Din.put(a,X)) :- X is Din.a + 1.
twice_a(Din,Din.put(a,X)) :- X is Din.a * 2.
nop(Din,Din).
write_then_mod(ModOp,DictFromLeft,DictToRight) :-
writeln(DictFromLeft.a),
call(ModOp,DictFromLeft,DictToRight).
main :-
express(_{a:1},[inc_a,twice_a,nop],_DictOut).
express(DictIn,ModOps,DictOut) :-
foldl(
write_then_mod, % will be called with args in correct order
ModOps,
DictIn,
DictOut).
Does it work?
?- main.
1
2
4
true.
Is it useful? It's definitely flexible:
?- express(_{a:1},[inc_a,twice_a,twice_a,inc_a,nop],_DictOut).
1
2
4
8
9
_DictOut = _9368{a:9}.
This is how you would type this in using Prolog syntax:
example(X, Y) :-
X = 1,
succ(X, Y).
If it is something else you are trying to achieve, you need to explain better. "How do I type it in Prolog" comes strictly after "What am I doing?"
Or is it that you really want this kind of syntactic nesting in Prolog? Could you provide a couple of examples where you think it is beneficial?
It's possible to define a let predicate that recursively replaces nested let expressions, so that local variables can be "redefined" without being renamed. This is one way to implement it:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
replace(Subterm0, Subterm, Term0, Term) :-
( Term0 == Subterm0 -> Term = Subterm
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist(replace(Subterm0,Subterm), Args0, Args),
Term =.. [F|Args]
).
replace_let(Term0, Term) :-
( [Term0,Term1] = [A,(A2 is B1, C2)],
(Pattern = (A1 is B1);Pattern = (A1 = B1)),
P1 = let(Pattern,C1),
subsumes_term(P1,A),
P1=A,
replace(A1,A2,C1,C2),
replace_let(Term1,Term)
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist(replace_let, Args0, Args),
Term =.. [F|Args]
).
let(A,B) :- replace_let(let(A,B),C),call(C).
main :-
B = 3,
let(A is B+1,(
writeln(A),
let(A is A + 1,(
writeln(A),
C is A + 1,
let(A = C,(
writeln(A)
))
))
)).
This implementation still doesn't work with "simultaneous" variable definitions, but the replace/2 predicate could easily be modified to replace several variables simultaneously.
My code:
run_insert_format([]).
run_insert_format([H|T]) :- H = [X,Y],
assertz(( X :- (verificar(Y)) )), run_insert_format(T).
run_query :-
odbc_query(
bd,
sqlQueryString,
List,
[ findall([Animal,Characteristic],row(Animal,Characteristic)) ]),
run_insert_format(List).
The List variable is giving me something like this:
[ [chita, mamifero], [chita, carnivoro], [chita, color_rojizo] ]
I'm trying to get this as a result of the asssertz:
%Reglas (rules)
% chita :-
% verificar(mamifero),
% verificar(carnivoro),
% verificar(color_rojizo).
But instead I get this:
% chita :-
% verificar(mamifero).
% chita :-
% verificar(carnivoro).
% chita :-
% verificar(color_rojizo).
I know what the code is doing correctly, but
What can I do to get the second result?.
Thanks in advance
To restate the problem, for each head foo, you're asserting a clause for every condition f1, f2, i.e.,
foo :- f1.
foo :- f2.
but what you want is to assert a single clause for each head with all conditions conjoined into a single body:
foo :- f1, f2.
The key step you're missing is converting the collection of characteristics for each animal into a conjunction, which you can then assert as the body.
Here is one way to achieve this result using swi-prolog association lists for grouping the characteristics:
:- use_module(library(assoc)).
assert_characteristics(Characteristics) :-
group_characteristics(Characteristics, GroupedCharacteristics),
maplist(assert_condition, GroupedCharacteristics).
% Conditions should be a conjunction, e.g., `(a, b, c)`
assert_condition(Head-Conditions) :-
maplist(make_check, Conditions, Checks),
list_to_conjunction(Checks, Conjunctions),
assertz(( Head :- Conjunctions )).
% ex. group_characteristics([foo-f1, foo-f2, bar-b1], [foo-[f1,f2], bar-[b1]]).
group_characteristics(AnimalCharacteristics, Grouped) :-
empty_assoc(Assoc),
group_characteristics(AnimalCharacteristics, Assoc, Grouped).
% helper for group_characteristics/2
group_characteristics([], Assoc, Grouped) :- assoc_to_list(Assoc, Grouped).
group_characteristics([Animal-Char|Rest], Assoc0, Grouped) :-
% Updating an existing animal with the new characteristic `Char`
( get_assoc(Animal, Assoc0, Chars, Assoc1, [Char|Chars]), !
% Otherwise, the key for `Animal` isn't present yet, so add it.
; put_assoc(Animal, Assoc0, [Char], Assoc1) ),
group_characteristics(Rest, Assoc1, Grouped).
% Convert a list of predictes into a conjunction of predicates
% ex. list_to_conjunction([a,b,c], (a, (b, (c, true)))).
list_to_conjunction([], true).
list_to_conjunction([P|Ps], (P, Conjuncts)) :- list_to_conjunction(Ps, Conjuncts).
% just a helper used in assert_condition/1
make_check(C, verificar(C)).
Example usage:
?- assert_characteristics([foo-foo1, bar-bar1, foo-foo2]).
true.
?- listing(foo).
:- dynamic foo/0.
foo :-
verificar(foo2),
verificar(foo1),
true.
There are a number of improvements and optimizations to be added to my example, such as making the grouping operation pure or collecting the characteristics into a set to prevent redundant conditions being included in the bodies.
sisters(mary,catherine).
sisters(catherine,mary).
brothers(john,simone).
brothers(simone,john).
marriage(john,mary,2010).
marriage(mary,john,2010).
marriage(kate,simone,2009).
marriage(simone,kate,2009).
marriage(catherine,josh,2011).
marriage(josh,catherine,2011).
birth(mary,johnny).
birth(mary,peter).
birth(catherine,william).
birth(kate,betty).
givebirthyear(mary,peter,2015).
givebirthyear(mary,johnny,2012).
givebirthyear(catherine,william,2012).
givebirthyear(kate,betty,2011).
siblings(X,Y) :-
birth(Parent,X),
birth(Parent,Y).
cousins(X,Y) :-
birth(Xparent,X),
birth(Yparent,Y),
sisters(Xparent,Yparent).
cousins(X,Y) :-
X \= Y,
birth(Xmom,X),
birth(Ymom,Y),
marriage(Xmom,Xdad,_),
marriage(Ymom,Ydad,_),
brothers(Xdad,Ydad).
I don' know what's happening in my code. When I input
cousins(betty,johnny).
and
cousins(william,johnny).
The prolog says true. But when I entered
cousins(S,johnny).
THe prolog says S = william but didn't show me that S = betty. I don't really know what's happening. Need help.
Here is the prolog result I got.
?- cousins(S,johnny).
S = william ;
false.
?- cousins(betty,johnny).
true.
?- cousins(william,johnny).
true .
The problem
The reason this happens is because
X \= Y,
actually means:
\+(X = Y).
now \+ or not in Prolog has some weird behaviour compared to the logical not. \+ means negation as finite failure. This means that \+(G) is considered to be true in case Prolog queries G, and can not find a way to satisfy G, and that G is finite (eventually the quest to satisfy G ends).
Now if we query \+(X = Y), Prolog will thus aim to unify X and Y. In case X and Y are (ungrounded) variables, then X can be equal to Y. As a result X \= Y fails in case X and Y are free variables.
So basically we can either use another predicate that for instance puts a constraint on the two variables that is triggered when the variables are grounded, or we can reorder the body of the clause, such that X and Y are already grounded before we call X \= Y.
If we can make for instance the assumption that X and Y will be grounded after calling birth/2, we can reorder the clause to:
cousins(X,Y) :-
birth(Xmom,X),
birth(Ymom,Y),
X \= Y,
marriage(Xmom,Xdad,_),
marriage(Ymom,Ydad,_),
brothers(Xdad,Ydad).
Prolog has however a predicate dif/2 that puts a constraint on the two variables, and from the moment the two are grounded, it will fail if the two are equal. So we can use it like:
cousins(X,Y) :-
dif(X,Y),
birth(Xmom,X),
birth(Ymom,Y),
marriage(Xmom,Xdad,_),
marriage(Ymom,Ydad,_),
brothers(Xdad,Ydad).
Making things simpler
That being said, I think you make the program too complex. We can start with a few definitions:
two people are slibings/2 if they are brothers/2 or sisters/2.
slibings(X,Y) :-
brothers(X,Y).
slibings(X,Y) :-
sisters(X,Y).
It is however possible that brothers/2 and sisters/2 do not provide all information. Two people are also slibings if they have the same mother (we will assume that people do not divorce here, or at least not give birth to other children after they remarry).
slibings(X,Y) :-
dif(X,Y),
birth(Mother,X),
birth(Mother,Y).
a parent/2 of a person is the mother of the person or the father (the person that married the mother).
So we can write:
parent(Mother,X) :-
birth(Mother,X).
parent(Father,X) :-
birth(Mother,X),
marriage(Father,Mother,_).
based on your example, the marriage/3 predicate is bidirectional: in case marriage(X,Y,Z)., then there is also a fact marriage(Y,X,Z)..
And now we can define:
two people are cousins if there parents are slibings:
cousins(X,Y) :-
parent(MF1,X),
parent(MF2,Y),
slibings(MF1,MF2).
and that's it.
I am doing Prolog Programming for my research and I got some problems..
First, all of my codes are below.
%% Lines are without period(.)
diagnosis :-
readln(Line1),
readln(Line2),
readln(Line3),
readln(Line4),
readln(Line5),
readln(Line6),
readln(Line7),
readln(Line8),
readln(Line9),
readln(Line10),
write(Line1),nl,
write(Line2),nl,
write(Line3),nl,
write(Line4),nl,
write(Line5),nl,
write(Line6),nl,
write(Line7),nl,
write(Line8),nl,
write(Line9),nl,
write(Line10),nl.
%% (get_symptom(Line1,[man]) -> write('man!!!!!!!!!')),
%% (get_symptom(Line2,[woman]) -> write('woman!!!!!!!!!')).
%% if A then B else C, (A->B; C)
%% grammar
s --> np, vp.
np --> det, n.
vp --> v, np.
det --> [a].
n --> [man].
v --> [has].
n --> [woman].
n --> [fever].
n --> [runny_nose].
get_symptom(Line,N) :- s(Line,[]), member(N,Line).
member(X, [X|T]).
member(X,[H|T]) :-
member(X,T).
%% FindSymptom(Line, [Symptom]) : - s(Line,[]), np(_, _, object,[a,
%% Symptom]), n(singular, [Symptom], []).
start :-
write('What is the patient''s name? '),
readln(Patient), %% Here, this can be used for input of all symtoms
diagnosis,
hypothesis(Patient,cold,S1),
append([cold/S1/red],[],N1), write(S1),
write('until...'),
hypothesis(Patient,severe_cold,S2), write(S2),
append([severe_cold/S2/red],N1,BarList),
write('until...'),
%% write(Patient,"probably has ",Disease,"."),nl.
hypothesis(Patient,Disease,100),
write(Patient),
write(' probably has '),
write(Disease),
write('.'),
test_barchart(BarList).
start :-
write('Sorry, I don''t seem to be able to'),nl,
write('diagnose the disease.'),nl.
symptom(Patient,fever) :-
get_symptom(Line1, [fever]);
get_symptom(Line2, [fever]);
get_symptom(Line3, [fever]);
get_symptom(Line4, [fever]);
get_symptom(Line5, [fever]);
get_symptom(Line6, [fever]);
get_symptom(Line7, [fever]);
get_symptom(Line8, [fever]);
get_symptom(Line9, [fever]);
get_symptom(Line10, [fever]).
symptom(Patient,runny_nose) :-
get_symptom(Line1, [runny_nose]);
get_symptom(Line2, [runny_nose]);
get_symptom(Line3, [runny_nose]);
get_symptom(Line4, [runny_nose]);
get_symptom(Line5, [runny_nose]);
get_symptom(Line6, [runny_nose]);
get_symptom(Line7, [runny_nose]);
get_symptom(Line8, [runny_nose]);
get_symptom(Line9, [runny_nose]);
get_symptom(Line10, [runny_nose]).
hypothesis(Patient,cold,Score_Cold) :-
(symptom(Patient,fever), Score_Cold is 100),write('!!!');
Score_Cold is 0.
hypothesis(Patient,severe_cold,Score_Severe) :-
((symptom(Patient,fever), Score1 is 50);
Score1 is 0),
((symptom(Patient, runny_nose), Score2 is 50);
Score2 is 0),
Score_Severe is Score1 + Score2.
%% hypothesis(Disease) :-
%%(hypothesis1(Patient,cold,Score1),
%%Score1 =:= 100 -> Disease = cold);
%%(hypothesis2(Patient,severe_cold,Score2),
%%Score2 =:= 100 -> Disease = severe_cold).
%% make graph for the result
:- use_module(library(pce)).
:- use_module(library(plot/barchart)).
:- use_module(library(autowin)).
test_barchart(BarList):-
new(W, picture),
send(W, display, new(BC, bar_chart(vertical,0,100))),
forall(member(Name/Height/Color,
BarList),
( new(B, bar(Name, Height)),
send(B, colour(Color)),
send(BC, append, B)
)),
send(W, open).
%% [X/100/red, y/150/green, z/80/blue, v/50/yellow]
%% append List
append([], L, L).
append([H|T], L2, [H|L3]):-
append(T, L2, L3).
As you can see, I want to make a bar_graph from 10 input lines by extracting symptoms..
But when I executed this code, I got the result as below...
1 ?- start.
What is the patient's name? GJ
|: a man has a runny_nose
|: a
|: a
|: a
|: a
|: a
|: a
|: a
|: a
|: a
[a,man,has,a,runny_nose]
[a]
[a]
[a]
[a]
[a]
[a]
[a]
[a]
[a]
!!!100until...100until...!!![GJ] probably has cold.
true
I only typed one symptom (runny_nose). I want to get Score for "cold" is 0, Score for "severe_cold" is 50 and BarGraph result... But What Happened? I can't find..
*****Edited******
I found that the problem is related to local variables (Line1, .. ,Line10) But how can I deal with? If I can make all the variables; Line1, ... , Line10 as global variables then, I think the problem can be solved...
****Addition*****
I changed my 'start' predicate as follows...I didn't use 'diagnosis' and 'hypothesis' predicates/ But the problems is maybe.. 'get_symptoms' predicate. Is there any choice I can choose except that I don't use 'get_symptoms' and 'symptoms' predicates...? Then the code will become very coarse...
start :-
write('What is the patient''s name? '),
readln(Patient), %% Here, this can be used for input of all symtom
readln(Line1),
readln(Line2),
readln(Line3),
readln(Line4),
readln(Line5),
readln(Line6),
readln(Line7),
readln(Line8),
readln(Line9),
readln(Line10),
(symptom(Patient,fever,Line1,Line2,Line3,Line4,Line5,Line6,Line7,Line8,Line9,Line10) -> (Cold is 80, Severe_Cold is 50)),
(symptom(Patient,runny_nose,Line1,Line2,Line3,Line4,Line5,Line6,Line7,Line8,Line9,Line10) -> Severe_Cold is Severe_Cold + 50),
write(Severe_Cold), write(Cold),
append([cold/Cold/red],[],N1),
append([severe_cold/Severe_Cold/red],N1,BarList),
%% write(Patient,"probably has ",Disease,"."),nl.
write(Severe_Cold),
((Cold =:= 100 -> Disease = cold) ; (Severe_Cold =:= 100 -> Disease = severe_cold)),
write(Patient),
write(' probably has '),
write(Disease),
write('.'),
test_barchart(BarList).
When programming in Prolog, you need to do some research into the language to understand how it works. Many Prolog beginners make the mistake of learning a couple of snippets of Prolog logic and then apply what they know of other languages to attempt to create a valid Prolog programming. However, Prolog doesn't work like other common languages at all.
Regarding variables, there are no globals. Variables are always "local" to a predicate clause. A predicate clause is one of one or more clauses that describe a predicate. For example:
foo(X, Y) :- (some logic including X and Y).
foo(X, Y) :- (some other logic including X and Y).
foo(X, X) :- (some other logic including X).
These are three clauses describing the predicate foo/2. The value of X or Y instantiated in one clause isn't visible to the other clauses.
If you want to instantiate a variable in one predicate and use it in another, you have to pass it as a parameter:
foo([1,2,3,4], L),
bar(L, X).
Here, foo might instantiate L using some logic and based upon the instantiated value of [1,2,3,4] for the first argument. Then L (now instantiated) is passed as the first argument to the predicate bar.
If you need a value to be persistent as data, you could assert it as a fact as follows:
foo :-
(some logic that determines X),
assertz(my_fact(X)),
(more logic).
bar :-
my_fact(X), % Will instantiate X with what was asserted
(some logic using X).
This would work, but is not a desirable approach in Prolog to "fake" global variables. Asserting items into persistent data is designed for the purpose of maintaining a Prolog database of information.
So you can see that the logic you have involving Line1, Line2, ... will not work. One clue would be that you must have received many warnings about these variables being "singleton". You need to study Prolog a bit more, then recast your logic using that knowledge.
I have come across an unfamiliar bit of Prolog syntax in Lee Naish's paper Higher-order logic programming in Prolog. Here is the first code sample from the paper:
% insertion sort (simple version)
isort([], []).
isort(A.As, Bs) :-
isort(As, Bs1),
isort(A, Bs1, Bs).
% insert number into sorted list
insert(N, [], [N]).
insert(N, H.L, N.H.L) :-
N =< H.
insert(N, H.LO, H.L) :-
N > H,
insert(N, LO, L).
My confusion is with A.As in isort(A.As, Bs) :-. From the context, it appears to be an alternate cons syntax for lists, the equivalent of isort([A|As], Bs) :-.
As well N.H.L appears to be a more convenient way to say [N|[H|L]].
But SWI Prolog won't accept this unusual syntax (unless I'm doing something wrong).
Does anyone recognize it? is my hypothesis correct? Which Prolog interpreter accepts that as valid syntax?
The dot operator was used for lists in the very first Prolog system of 1972, written in Algol-W, sometimes called Prolog 0. It is inspired by similar notation in LISP systems. The following exemple is from the paper The birth of Prolog by Alain Colmerauer and Philippe Roussel – the very creators of Prolog.
+ELEMENT(*X, *X.*Y).
+ELEMENT(*X, *Y.*Z) -ELEMENT(*X, *Z).
At that time, [] used to be NIL.
The next Prolog version, written in Fortran by Battani & Meloni, used cases to distinguish atoms and variables. Then DECsystem 10 Prolog introduced the square bracket notation replacing nil and X.Xs with [] and [X,..Xs] which in later versions of DECsystem 10 received [X|Xs] as an alternative. In ISO Prolog, there is only [X|Xs], .(X,Xs), and as canonical syntax '.'(X,Xs).
Please note that the dot has many different rôles in ISO Prolog. It serves already as
end token when followed by a % or a layout character like SPACE, NEWLINE, TAB.
decimal point in a floating point number, like 3.14159
graphic token char forming graphic tokens as =..
So if you are now declaring . as an infix operator, you have to be very careful. Both with what you write and what Prolog systems will read. A single additional space can change the meaning of a term. Consider two lists of numbers in both notations:
[1,2.3,4]. [5].
1 .2.3.4.[]. 5.[].
Please note that you have to add a space after 1. In this context, an additional white space in front of a number may change the meaning of your terms. Like so:
[1|2.3]. [4]. 5. [].
1 .2.3. 4.[]. 5. [].
Here is another example which might be even more convincing:
[1,-2].
1.(-2).[].
Negative numbers require round brackets within dot-lists.
Today, there is only YAP and XSB left that still offer infix . by default – and they do it differently. And XSB does not even recognize above dot syntax: you need round brackets around some of the nonnegative numbers.
You wrote that N.H.L appears to be a more convenient way to say [N|[H|L]]. There is a simple rule-of-thumb to simplify such expressions in ISO Prolog: Whenever you see within a list the tokens | and [ immediately after each other, you can replace them by , (and remove the corresponding ] on the right side). So you can now write: [N,H|L] which does not look that bad.
You can use that rule also in the other direction. If we have a list [1,2,3,4,5] we can use | as a "razor blade" like so: [1,2,3|[4,5]].
Another remark, since you are reading Naish's paper: In the meantime, it is well understood that only call/N is needed! And ISO Prolog supports call/1, call/2 up to call/8.
Yes, you are right, the dot it's the list cons infix operator. It's actually required by ISO Prolog standard, but usually hidden. I found (and used) that syntax some time ago:
:- module(eog, []).
:- op(103, xfy, (.)).
% where $ARGS appears as argument, replace the call ($ARGS) with a VAR
% the calle goes before caller, binding the VAR (added as last ARG)
funcs(X, (V, Y)) :-
nonvar(X),
X =.. W.As,
% identify meta arguments
( predicate_property(X, meta_predicate M)
% explicitly exclude to handle test(dcg)
% I'd like to handle this case in general way...
, M \= phrase(2, ?, ?)
-> M =.. W.Ms
; true
),
seek_call(As, Ms, Bs, V),
Y =.. W.Bs.
% look for first $ usage
seek_call([], [], _Bs, _V) :-
!, fail.
seek_call(A.As, M.Ms, A.Bs, V) :-
M #>= 0, M #=< 9, % skip meta arguments
!, seek_call(As, Ms, Bs, V).
seek_call(A.As, _, B.As, V) :-
nonvar(A),
A = $(F),
F =.. Fp.FAs,
( current_arithmetic_function(F) % inline arith
-> V = (PH is F)
; append(FAs, [PH], FBs),
V =.. Fp.FBs
),
!, B = PH.
seek_call(A.As, _.Ms, B.As, V) :-
nonvar(A),
A =.. F.FAs,
seek_call(FAs, Ms, FBs, V),
!, B =.. F.FBs.
seek_call(A.As, _.Ms, A.Bs, V) :-
!, seek_call(As, Ms, Bs, V).
:- multifile user:goal_expansion/2.
user:goal_expansion(X, Y) :-
( X = (_ , _) ; X = (_ ; _) ; X = (_ -> _) )
-> !, fail % leave control flow unchanged (useless after the meta... handling?)
; funcs(X, Y).
/* end eog.pl */
I was advised against it. Effectively, the [A|B] syntax it's an evolution of the . operator, introduced for readability.
OT: what's that code?
the code above it's my attempt to sweeten Prolog with functions. Namely, introduces on request, by means of $, the temporary variables required (for instance) by arithmetic expressions
fact(N, F) :-
N > 1 -> F is N * $fact($(N - 1)) ; F is 1.
each $ introduce a variable. After expansion, we have a more traditional fact/2
?- listing(fact).
plunit_eog:fact(A, C) :-
( A>1
-> B is A+ -1,
fact(B, D),
C is A*D
; C is 1
).
Where we have many expressions, that could be useful...
This syntax comes from NU-Prolog. See here. It's probably just the normal list functor '.'/2 redefined as an infix operator, without the need for a trailing empty list:
?- L= .(a,.(b,[])).
L = [a,b]
Yes (0.00s cpu)
?- op(500, xfy, '.').
Yes (0.00s cpu)
?- L = a.b.[].
L = [a,b]
Yes (0.00s cpu)