%! \begin{jseval} async(pgcell) => { pgcell.pgset.main_doc.set_slide_mode(true); } %! \end{jseval}
¹ Universidad Politécnica de Madrid (UPM)
² IMDEA Software Institute
³ Spanish Council for Scientific Research (CSIC)
%! \begin{jseval} async (pg) => { let e = pg.preview_el; e.innerHTML = ` Thanks to David S. Warren and Daniela Ferreiro for their feedback. <BR> <h2>Prolog Education Workshop @ ICLP. October 13, 2024. Dallas</h2> <small> `; pg.show_preview(true); pg.update_inner_layout(); } %! \end{jseval}
This presentation corresponds to the paper Teaching Pure LP with Prolog and a Fair Search Rule, in the Prolog Education Group's (PEG 2.0) 2024 Workshop at ICLP 2024.
See also our Course material on logic and constraint logic programming page for more details.
These slides are an Active Logic Document which can be edited using the Ciao Prolog Playground.
:- module(_,_). %! \begin{focus} mother_of(susan, mary). mother_of(susan, john). mother_of(mary, michael). father_of(john, david). grandmother_of(X,Y) :- mother_of(X,Z), mother_of(Z,Y). grandmother_of(X,Y) :- mother_of(X,Z), father_of(Z,Y). %! \end{focus}Students can get answers to questions in different modes:
?- mother_of(susan,Y).
?- mother_of(X,mary).
?- grandmother_of(X,Y).All is good!
:- module(_,_). %! \begin{focus} resistor(power,n1). resistor(power,n2). transistor(n2,ground,n1). transistor(n3,n4,n2). transistor(n5,ground,n4). inverter(Input,Output) :- transistor(Input,ground,Output), resistor(power,Output). nand_gate(Input1,Input2,Output) :- transistor(Input1,X,Output), transistor(Input2,ground,X), resistor(power,Output). and_gate(Input1,Input2,Output) :- nand_gate(Input1,Input2,X), inverter(X, Output). %! \end{focus}
?- and_gate(In1,In2,Out).All still good!
All these queries will return the correct solution and terminate using standard Prolog.
However, things can quickly get more complicated...
:- module(_,_). %! \begin{focus} mother_of(susan, mary). mother_of(susan, john). mother_of(mary, michael). father_of(john, david). parent(X,Y) :- mother_of(X,Y). parent(X,Y) :- father_of(X,Y). ancestor(X,Y) :- ancestor(X,Z), parent(Z,Y). ancestor(X,Y) :- parent(X,Y). %! \end{focus}Now, for any query, standard Prolog hangs:
?- ancestor(X,Y).
:- module(_,_). %! \begin{focus} mother_of(susan, mary). mother_of(susan, john). mother_of(mary, michael). father_of(john, david). parent(X,Y) :- mother_of(X,Y). parent(X,Y) :- father_of(X,Y). ancestor(X,Y) :- parent(X,Y). ancestor(X,Y) :- ancestor(X,Z), parent(Z,Y). %! \end{focus}Changing the order of the ancestor/2 clauses:
?- ancestor(X,Y).Standard Prolog obtains all the answers and then hangs.
All sizes of subgoals and answers < some fixed number.
(This is the case for all ``Datalog'' programs.)
All sizes of subgoals and answers < some fixed number.
(This is the case for all ``Datalog'' programs.)
:- table ancestor/2.
?- ancestor(X,Y).Now we not only obtain all answers (even with the first clause order):
X = susan, Y = mary ? ; X = susan, Y = john ? ; X = mary, Y = michael ? ; X = john, Y = david ? ; X = susan, Y = michael ? ; X = susan, Y = david ? ; nobut the program also terminates.
We can get this result also with standard Prolog, but need to switch order of goals in the body:
:- module(_,_). %! \begin{focus} mother_of(susan, mary). mother_of(susan, john). mother_of(mary, michael). father_of(john, david). parent(X,Y) :- mother_of(X,Y). parent(X,Y) :- father_of(X,Y). ancestor(X,Y) :- parent(X,Y). ancestor(X,Y) :- parent(Z,Y), ancestor(X,Z). %! \end{focus}
?- ancestor(X,Y).However, this is not always the case and tabling offers a more powerful mechanism.
Some relevant issues in tabling:
:- module(_,_). %! \begin{focus} natural(0). natural(s(X)) :- natural(X). add(0,Y,Y) :- natural(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z). mult(0,Y,0) :- natural(Y). mult(s(X),Y,Z) :- add(W,Y,Z), mult(X,Y,W). nat_square(X,Y) :- natural(X), natural(Y), mult(X,X,Y). %! \end{focus}
:- module(_,_). %! \begin{focus} natural(0). natural(s(X)) :- natural(X). add(0,Y,Y) :- natural(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z). mult(0,Y,0) :- natural(Y). mult(s(X),Y,Z) :- add(W,Y,Z), mult(X,Y,W). nat_square(X,Y) :- natural(X), natural(Y), mult(X,X,Y). %! \end{focus}Standard Prolog execution provides useful answers for, e.g., mode nat_square(+,-).
?- nat_square(s(s(0)), X).and even for mode nat_square(-,+) which nicely surprises students:
?- nat_square(X,s(s(s(s(0))))).
?- nat_square(X,Y).
:- use_package(sr/bfall).
:- module(_,_). %! \begin{focus} natural(0). natural(s(X)) :- natural(X). add(0,Y,Y) :- natural(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z). mult(0,Y,0) :- natural(Y). mult(s(X),Y,Z) :- add(W,Y,Z), mult(X,Y,W). nat_square(X,Y) :- natural(X), natural(Y), mult(X,X,Y). %! \end{focus}
?- nat_square(X,Y).
:- module(_,_). %! \begin{focus} natural(0). natural(s(X)) :- natural(X). add(0,Y,Y) :- natural(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z). mult(0,Y,0) :- natural(Y). mult(s(X),Y,Z) :- add(W,Y,Z), mult(X,Y,W). %! \end{focus}
?- mult(X,Y,Z).
:- module(_,_). %! \begin{focus} :- use_package(sr/bfall). natural(0). natural(s(X)) :- natural(X). add(0,Y,Y) :- natural(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z). mult(0,Y,0) :- natural(Y). mult(s(X),Y,Z) :- add(W,Y,Z), mult(X,Y,W). %! \end{focus}
?- mult(X,Y,Z).
:- module(_,_). %! \begin{focus} :- use_package([assertions,regtypes]). % :- use_package(sr/bfall). :- regtype color/1. color(red). color(green). color(blue). :- regtype colorlist/1. colorlist([]). colorlist([H|T]) :- color(H), colorlist(T). %! \end{focus}
?- colorlist(L).
:- module(_,_). %! \begin{focus} :- use_package([fsyntax,assertions,regtypes]). % :- use_package(sr/bfall). :- regtype color/1. color := red | green | blue. :- regtype colorlist/1. colorlist := [] | [~color|~colorlist]. %! \end{focus}
?- colorlist(L).
''Z is the remainder from dividing X by Y''
:- module(_,_). %! \begin{focus} :- use_package(sr/bfall). mod(X,Y,Z) :- less(Z, Y), mult(Y,_Q,W), add(W,Z,X). %! \end{focus}
?- op(500,fy,s). ?- mod(X,Y, s 0).
%! \begin{jseval} async (pg) => { let e = pg.preview_el; e.innerHTML = ` <br> <br> <br> <br> <br> <center> <h1>Thanks!</h1> </center> `; pg.show_preview(true); pg.update_inner_layout(); } %! \end{jseval}
%! \begin{jseval} async (pg) => { let e = pg.preview_el; e.innerHTML = ` <br> <br> <br> <br> <br> <center> <h1>Additional slides</h1> </center> `; pg.show_preview(true); pg.update_inner_layout(); } %! \end{jseval}
polynomial(X,X). polynomial(Term,X) :- pconstant(Term). polynomial(Term1+Term2,X) :- polynomial(Term1,X), polynomial(Term2,X). polynomial(Term1-Term2,X) :- polynomial(Term1,X), polynomial(Term2,X). polynomial(Term1*Term2,X) :- polynomial(Term1,X), polynomial(Term2,X). polynomial(Term1/Term2,X) :- polynomial(Term1,X), pconstant(Term2). polynomial(Term1^N,X) :- polynomial(Term1,X), nat(N).
deriv(X,X,s(0)). deriv(C,X,0) :- pconstant(C). deriv(U+V,X,DU+DV) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U-V,X,DU-DV) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U*V,X,DU*V+U*DV) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U/V,X,(DU*V-U*DV)/V^s(s(0))) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U^s(N),X,s(N)*U^N*DU) :- deriv(U,X,DU), nat(N). deriv(log(U),X,DU/U) :- deriv(U,X,DU). ...
where q0 is both the initial and the final state.
initial(q0). delta(q0,a,q1). delta(q1,b,q0). final(q0). delta(q1,b,q1). accept(S) :- initial(Q), accept_from(S,Q). accept_from([],Q) :- final(Q). accept_from([X|Xs],Q) :- delta(Q,X,NewQ), accept_from(Xs,NewQ).
accept(S) :- initial(Q), accept_from(S,Q,[]). accept_from([],Q,[]) :- final(Q). accept_from([X|Xs],Q,S) :- delta(Q,X,S,NewQ,NewS), accept_from(Xs,NewQ,NewS). initial(q0). final(q1). delta(q0,X,Xs,q0,[X|Xs]). delta(q0,X,Xs,q1,[X|Xs]). delta(q0,X,Xs,q1,Xs). delta(q1,X,[X|Xs],q1,Xs).
is represented by:
hanoi_moves( s(s(s(0))),
[ move(a,b), move(a,c), move(b,c), move(a,b),
move(c,a), move(c,b), move(a,b) ])
''Moves contains the moves needed to move a tower of Ndisks from peg Orig to peg Dest, with the help of peg Help.''
hanoi(s(0),Orig,Dest,_Help,[move(Orig, Dest)]). hanoi(s(N),Orig,Dest,Help,Moves) :- hanoi(N,Orig,Help,Dest,Moves1), hanoi(N,Help,Dest,Orig,Moves2), append(Moves1,[move(Orig, Dest)|Moves2],Moves).
hanoi_moves(N,Moves) :- hanoi(N,a,b,c,Moves).
ackermann(0,N) = N+1 ackermann(M,0) = ackermann(M-1,1) ackermann(M,N) = ackermann(M-1,ackermann(M,N-1))
ackermann(0,N) = s(N) ackermann(s(M1),0) = ackermann(M1,s(0)) ackermann(s(M1),s(N1)) = ackermann(M1,ackermann(s(M1),N1))
ackermann(0,N,s(N)). ackermann(s(M1),0,Val) :- ackermann(M1,s(0),Val). ackermann(s(M1),s(N1),Val) :- ackermann(s(M1),N1,Val1), ackermann(M1,Val1,Val).
:- use_package(functional). :- op(500,fy,s). ackermann( 0, N) := s N. ackermann(s M, 0) := ackermann(M, s 0). ackermann(s M, s N) := ackermann(M, ackermann(s M, N) ).
nat(0). nat(s(X)) :- nat(X).Using special := notation for the ''return'' (last) argument:
nat := 0. nat := s(X) :- nat(X).
nat := 0. nat := s(~nat).''~'' not needed with functional package if inside its own definition:
nat := 0. nat := s(nat).Using an :- op(500,fy,s). declaration to define s as a prefix operator:
nat := 0. nat := s nat.Using ''|'' (disjunction):
nat := 0 | s nat.Which is exactly equivalent to:
nat(0). nat(s(X)) :- nat(X).