%! \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. <BR> <h2>Prolog Education Workshop @ ICLP. October 13, 2024. Dallas</h2> `; pg.show_preview(true); pg.update_inner_layout(); } %! \end{jseval}
:- 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 = ` <h1>Thanks!</h1> `; 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).
- Classical installation: most appropriate for more advanced students / "real" use. Show serious, competitive language. - Playgrounds and notebooks --see PL50 Book papers! (e.g., Ciao Playground/Active Logic Documents, SWISH, $\tau$-Prolog, SICStus+Jupyter). - Can be attractive for beginners, young students. - Some places (e.g., schools) may not have personnel/machines for installation, but will have a tablet. - Server-based vs. browser-based. - Very useful for executable examples in manuals and tutorials. Offer both types to students!
- pure LP (with several search rules, tabling), - ISO-Prolog, - higher-order support and functional syntax, - constraints, - ASP/s(CASP), etc.
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 funcional 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).