// Automatic presentation mode (even from playground) if (main_doc !== pg.pgset.main_doc) main_doc.pgset.cells[0].toggle_presentation(); // Set slideshow mode pg.pgset.main_doc.set_slide_mode(true);
// Changing some fonts and spacing from lpdoc.css
const cssString = `
/* A bit less space before and after the title */
div.lpdoc-cover-title {
margin-top: -20px;
margin-bottom: -20px;
}
/* A bit more space on slides */
.lpdoc-slide-mode .lpdoc-slide {
width: calc(100% - 32px);
height: calc(100% - 0px);
/* T R B L */
padding: 0px 16px 0px 16px;
}
/* A bit less space for slide titles */
h2 {
line-height: 1em;
margin-bottom: 0.3em;
}
/* A bit less space aroud the notes */
.lpdoc-note {
/* T R B L */
padding: 4px 8px 4px 8px;
}
/* A bit less space above and below the runnables */
.lpdoc-runnable-container {
position: relative;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.lpdoc-runnable-editor-container,
.lpdoc-runnable-container .comint-editor-container {
border: 1px solid var(--codeblock-border);
border-radius: 10px;
padding-top: 4px;
padding-bottom: 4px;
background: var(--codeblock-bg);
}
`;
const style_el = document.createElement("style");
style_el.textContent = cssString;
document.head.appendChild(style_el); ¹ Universidad Politécnica de Madrid (UPM)
² IMDEA Software Institute
³ Spanish Council for Scientific Research (CSIC)
// Injecting arbitrary HTML (needs better interface of course) let e = pg.preview_el; e.innerHTML = ` <small> With thanks to David S. Warren and Daniela Ferreiro for their feedback. </small> `; pg.show_preview(true); pg.update_inner_layout();
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). % To load the program, we click on the '?' --->
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} ?- mother_of(susan,Y).
?- mother_of(X,mary).
?- grandmother_of(X,Y).
:- module(_,_).
:- discontiguous(resistor/2).
:- discontiguous(transistor/3).
%! \begin{focus}
resistor(power,n1). transistor(n2,ground,n1).
resistor(power,n2). transistor(n3,n4,n2).
transistor(n5,ground,n4).
inverter(In,Out) :- transistor(In,ground,Out), resistor(power,Out).
nand_gate(In1,In2,Out) :- transistor(In1,X,Out), transistor(In2,ground,X), resistor(power,Out).
and_gate(In1,In2,Out) :- nand_gate(In1,In2,X), inverter(X, Out).
%! \end{focus}?- and_gate(In1,In2,Out).All still good: all queries will return the correct solution and terminate in 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} ?- 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} ?- ancestor(X,Y).
:- table ancestor/2. :- table ancestor/2.X = susan, Y = mary ? ; X = susan, Y = john ? ; X = mary, Y = michael ? ; X = john, Y = david ? ; X = susan, Y = michael ? ; X = susan, Y = david ? ; no
:- 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).
natural(0). natural(s(X)) :- natural(X). add(0,Y,Y) :- natural(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z).
natural(0). natural(s(0)). natural(s(s(0))). ...
natural(0). natural(s(X)) :- natural(X).
:- 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).
the ``quality'' (fairness) in the solution enumeration greatly improves the answers!
:- 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).
the ``quality'' (fairness) in the solution enumeration greatly improves the answers!
:- 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([assertions, regtypes, fsyntax]).
% :- use_package(sr/bfall).
:- regtype color/1.
color := red | green | blue.
:- regtype colorlist/1.
colorlist := [] | [~color|~colorlist].
%! \end{focus}?- colorlist(L).
We cannot completely eradicate non-termination!
''Z is the remainder from dividing X by Y''
can be expressed directly in Prolog:
:- module(_,_,[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).
less(0,s(X)):- natural(X).
less(s(X),s(Y)) :- less(X,Y).
%! \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).
for the first parts of the course.
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();
let e = pg.preview_el; e.innerHTML = ` <br> <br> <br> <br> <br> <center> <h1>Additional examples and discussion</h1> </center> `; pg.show_preview(true); pg.update_inner_layout();
:- module(_,_,[sr/bfall]).
natural(0).
natural(s(X)) :- natural(X).
%! \begin{focus}
:- use_package(sr/bfall).
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), natural(N).
pconstant(a). pconstant(b). pconstant(c).
%! \end{focus}?- polynomial(P,x).
:- module(_,_,[sr/bfall]).
natural(0).
natural(s(X)) :- natural(X).
%! \begin{focus}
:- use_package(sr/bfall).
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), natural(N).
deriv(log(U),X,DU/U) :- deriv(U,X,DU).
% ...
pconstant(a). pconstant(b). pconstant(c).
pconstant(X) :- natural(X).
%! \end{focus}?- deriv( s(s(s(0)))*x+s(s(0)), x, D).
?- deriv(I, x, 0*x+s(s(s(0)))*s(0)+0).
where q0 is both the initial and the final state.
:- module(_,_,[sr/bfall]).
%! \begin{focus}
:- use_package(sr/bfall).
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([C|Cs],Q) :- delta(Q,C,NewQ), accept_from(Cs,NewQ).
%! \end{focus} ?- accept([a,b,b]).
?- accept([a,b,b,a]).
?- accept([A, B, C, D]).
?- accept(X).
:- module(_,_,[sr/bfall]).
%! \begin{focus}
:- use_package(sr/bfall).
accept(S) :- initial(Q), accept_from(S, Q, []).
accept_from([], Q, [] ) :- final(Q).
accept_from([C|Cs], Q, Stack) :-
delta(Q, C, Stack, NewQ, NStack), accept_from(Cs, NewQ, NStack).
initial(q0). final(q1).
delta(q0, C, Stack, q0, [C|Stack]).
delta(q0, C, Stack, q1, [C|Stack]).
delta(q0,_C, Stack, q1, Stack ).
delta(q1, C, [C|Stack], q1, Stack ).
%! \end{focus}?- accept([a,b,b,a]).
?- accept([a,b,c,b,a]).
?- accept([a,b,c,c,b,a]).
?- accept([a,b,X,a]).
?- accept([A, B, C, D]).
?- accept(X).
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.''
:- module(_,_).
%! \begin{focus}
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).
%! \end{focus} ?- hanoi_moves(s(s(s(0))),M).
?- hanoi_moves(N,M).
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).
:- module(_,_).
%! \begin{focus}
:- 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) ).
%! \end{focus}?- op(500,fy,s).(defines s as a prefix operator to save us writing parenthesis)
?- ackermann(s s 0, s s s 0, X).But now it also runs in other modes:
?- ackermann(s s 0, s s s 0, s s s s s s s s s 0).
?- ackermann(s s 0, Y, s s s s s s s s s 0).
?- ackermann(X, s s s 0, s s s s s s s s s 0).
nat(0). nat(s(X)) :- nat(X).
nat := 0. nat := s(X) :- nat(X).
nat := 0. nat := s(~nat).
nat := 0. nat := s(nat).
nat := 0. nat := s nat.
nat := 0 | s nat.
nat(0). nat(s(X)) :- nat(X).
Shows serious, competitive language.