\documentclass[11pt]{article}
\usepackage[margin=1.45in]{geometry}
\usepackage{proof}
\usepackage{amsmath,amsthm,amssymb}
\usepackage[raiselinks=false,colorlinks=true,citecolor=blue,urlcolor=blue,linkcolor=blue,bookmarksopen=true,dvips]{hyperref}
\usepackage{tabls}
\setlength{\inferLineSkip}{4pt}
% font{
\usepackage{pxfonts}
%% fix sans serif
\renewcommand\sfdefault{cmss}
\DeclareMathAlphabet{\mathsf}{OT1}{cmss}{m}{n}
\SetMathAlphabet{\mathsf}{bold}{OT1}{cmss}{b}{n}
% }font
\theoremstyle{definition}
\newtheorem{task}{Task}
\newtheorem{ectask}{Extra Credit Task}
\newtheorem*{theorem}{Theorem}
\newtheorem*{lemma}{Lemma}
\newcommand\pimp{\mathrel{\supset}}
\newcommand\pand{\mathrel{\wedge}}
\newcommand\por{\mathrel{\vee}}
\newcommand\ptrue{\top}
\newcommand\pfalse{\bot}
\newcommand\pnot{\neg}
\newcommand\pspades{\spadesuit}
\newcommand\pforall[3]{\forall #1{:}#2.\, #3}
\newcommand\pexists[3]{\exists #1{:}#2.\, #3}
\newcommand\qtri\blacktriangledown
\newcommand\ptri[3]{\qtri #1{:}#2.\, #3}
\newcommand\eqn{\mathrel{=_N}}
\newcommand\z{\mathsf{z}}
\newcommand\s{\mathsf{s}}
\newcommand\Cases{\mathit{Cases}}
\newcommand\ii{\mathsf{i}}
\newcommand\oo{\mathsf{o}}
\newcommand\true{\;\textit{true}}
\newcommand\false{\;\textit{false}}
\newcommand\contra{\#}
\newcommand\seq{\longrightarrow}
\newcommand\Seq{\Longrightarrow}
\newcommand\ddd{\raisebox{0.2em}[1.3em]{$\vdots$}}
\newcommand\com{\raisebox{0.3em}{$\ ,\ \ $}}
\newcommand\hyp[2][]{\infer[#1]{#2}{}}
\newcommand\sub[3]{\infer[#2]{#3}{#1}}
\newcommand\dec{{:}}
\newcommand\has{:}
\newcommand\semi{\,;\,}
\newcommand{\lred}{\mathrel{\raisebox{0.5em}{$\Longrightarrow_R$}}}
\newcommand{\lexp}{\mathrel{\raisebox{0.5em}{$\Longrightarrow_E$}}}
\newcommand{\DD}{\mathcal{D}}
\newcommand{\EE}{\mathcal{E}}
\newcommand{\FF}{\mathcal{F}}
\newcommand{\GG}{\mathcal{G}}
% some URLs used below
\newcommand\prologLectureURL{http://www.cs.cmu.edu/~fp/courses/15317-f09/lectures/14-prolog.pdf}
\newcommand\gprologManualURL{http://www.gprolog.org/manual/html_node/index.html}
\newcommand\gprologPredicatesURL{http://gprolog.org/manual/html_node/gprolog023.html}
\newcommand\bogosortURL{http://en.wikipedia.org/wiki/Bogosort}
\newcommand\shuntingYardURL{http://en.wikipedia.org/wiki/Shunting-yard_algorithm}
\title{Constructive Logic (15-317), Fall 2009 \\
Assignment 7: Programming in Prolog}
\author{William Lovas \texttt{(wlovas@cs)}}
\date{Out: Thursday, October 22, 2009 \\
Due: Thursday, October 29, 2009 (before class)}
\begin{document}
\maketitle
In this assignment, you'll practice programming in Prolog in order to
learn the idiosyncracies and idioms particular to the discipline of
logic programming. By the end of the assignment, you will be an
expert Prolog hacker, and the basic ideas you learn in Prolog will carry
over to other logic programming languages we encounter as the semester
progresses.
The written portion of your work (Section~\ref{sec:cuts}) may be
submitted at the beginning of class, typeset or handwritten, but
alternatively, you may submit the written portion in comments as part of
your programming portion.
% As usual, if you are familiar with
% \LaTeX, you are encouraged to use this document as a template for typesetting
% your solutions, but you may alternatively write your solutions \textit{neatly}
% by hand.
The programming portion of your work (Sections~\ref{sec:sorting}
and \ref{sec:arithmetic}) should be submitted via AFS by putting your code
in a file \verb'hw07.pl' and copying it to the directory
\begin{verbatim}
/afs/andrew/course/15/317/submit//hw07
\end{verbatim}
where \verb'' is replaced with your Andrew ID.
For the programming problems, try to make your code as clean and general as
possible, leveraging the intrinsic expressive power of logic programming to
obtain clear and elegant programs. You may find it useful to refer to the
\href{\gprologManualURL}{GNU Prolog Manual}, in particular the chapter on
\href{\gprologPredicatesURL}{Prolog built-in predicates}.
\section{One Cut, Two Cut, Red Cut, Green Cut (8 points)}
\label{sec:cuts}
Recall the Prolog control mechanism \verb'!', pronounced ``cut'': as a
subgoal, it always succeeds, but when it does so it has the side effect of
forcing Prolog's proof search to commit to all choices made thus far; proof
search never backtracks past a cut.
Remember also that cuts can be divided into \textit{red cuts} and
\textit{green cuts}. Green cuts do not affect the meaning of a program:
any predicate that holds when the cut is deleted still holds when the cut
is present. Red cuts, on the other hand, prune search that might lead to new
solutions, and in this way they change the meaning of the logic program.
Sometimes the question of whether a cut is red or green depends upon our
mode interpretation of the predicate in whose definition the cut appears.
\begin{task}[8 pts]
For each the following Prolog code fragments, identify the cut as
\textit{green} or \textit{red}. Justify your choice with a short
explanation, and be sure to state any assumptions you make.
\begin{center}
\begin{minipage}{0.95\textwidth}
\newcommand\fraghead[1]{\smallskip\hrule\smallskip\ \textbf{#1}\smallskip\hrule}
\fraghead{Fragment 1}
\begin{verbatim}
doFor(B, what) :- klondike_bar(B).
doFor(love, that) :- !, fail.
doFor(love, _).
\end{verbatim}
\fraghead{Fragment 2}
\begin{verbatim}
partition([], _, [], []).
partition([X|Xs], X0, [X|Ls], Gs) :-
X =< X0, !, partition(Xs, X0, Ls, Gs).
partition([X|Xs], X0, Ls, [X|Gs]) :-
X > X0, partition(Xs, X0, Ls, Gs).
\end{verbatim}
\fraghead{Fragment 3}
\begin{verbatim}
search(X, leaf(X)).
search(X, node(Left, Right)) :- search(X, Left), !.
search(X, node(Left, Right)) :- search(X, Right).
\end{verbatim}
\fraghead{Fragment 4}
\begin{verbatim}
classify(Msg, spam) :- is_spam(Msg), !.
classify(Msg, ham).
\end{verbatim}
\end{minipage}
\end{center}
\end{task}
\section{Sorting Lists (16 points)}
\label{sec:sorting}
In \href{\prologLectureURL}{Lecture Notes 14}, we saw how to code the
quicksort algorithm in Prolog. Here, we'll consider two other sorting
algorithms.
\subsection{Bogo sort}
Prolog can compactly and elegantly represent a sorting algorithm that is
even worse than insertion sort. The canonical
\href{\bogosortURL}{bogo sort} works by randomly permuting a list until it
becomes sorted. Here, we'll consider a deterministic variant that searches
for a sorted permutation systematically.
\begin{task}[6 pts]
Write a predicate \verb'sorted([integer])' which holds of any
$\leq$-ordered list of Prolog integers. Then write a predicate
\verb'perm([integer], [integer])' which holds whenever its first argument
is a permutation of its second. Use these together to implement a concise
version of deterministic bogo sort as a predicate
\verb'bogosort([integer], [integer])' that searches for a sorted
permutation of its first argument. Your predicate should be usable as a
sorting function, at least on very small inputs.
\end{task}
\subsection{Merge sort}
Prolog can also compactly and elegantly represent efficient sorting
algorithms like \textit{merge sort}. To merge sort a list, first split it
into two roughly equal halves; then, recursively merge sort the two halves;
finally merge the two sorted halves into a sorted list.
\begin{task}[10 pts]
Implement a predicate \verb'mergesort(+[integer], -[integer])' which merge
sorts a given list, returning the sorted list. Your predicate should be
usable as a sorting function on \textit{much} larger inputs than
\verb'bogosort'.
\end{task}
\section{Arithmetic Expressions (16 points)}
\label{sec:arithmetic}
Prolog can be used to do much more than list processing. Many symbolic
processing tasks can elegantly be written as Prolog programs, in much the
same way as symbolic tasks can elegantly be written as ML programs. Of
course, the logic programming paradigm changes the game quite a bit: an
elegant ML program might not transliterate directly to an elegant Prolog
program, nor vice versa.
In this section we'll explore symbolic processing tasks involving a small
language of arithmetic expressions with numerical constants, addition, and
multiplication.
\[
e ::= n \mid e_1 + e_2 \mid e_1 \times e_2
\]
The following Prolog type predicate recognizes expressions built using the
constructors \verb'eInt', \verb'ePlus', and \verb'eTimes':
\begin{verbatim}
exp(ePlus(E1, E2)) :- exp(E1), exp(E2).
exp(eTimes(E1, E2)) :- exp(E1), exp(E2).
exp(eInt(N)) :- integer(N).
\end{verbatim}
% \subsection{Evaluation}
\begin{task}[6 pts]
Write an evaluator predicate \verb'eval(+exp, -integer)' that evaluates
an arithmetic expression to its values.
\end{task}
\subsection{Parsing}
If we isolate certain terms as tokens, we can talk about writing a parser
for arithmetic expressions using Dijkstra's classic
\href{\shuntingYardURL}{shunting yard algorithm}.
We'll use the following constructors for tokens:
\begin{verbatim}
token(tLparen). token(tRparen). % parentheses
token(tPlus). token(tTimes). % operators
token(tInt(N)) :- integer(N). % integer constants
\end{verbatim}
A parser for arithmetic expressions is one that transforms a list of tokens
into an expression.
The shunting yard algorithm works using two stacks, a stack of operators
and a stack of operands. When we say
\begin{quote}
reduce an expression onto the operand stack,
\end{quote}
we mean
\begin{quote}
pop the top operator from the operator stack and the top two operands from
the operand stack, build an expression out of them and push that
expression onto the operand stack.
\end{quote}
With this in mind, we can describe (a simplified variant of) the algorithm
as follows. Repeatedly consider each token in the input list in turn:
\begin{itemize}
\item If the token is a number (\verb'tInt(N)' in our syntax), then remove
it from the input list and push it onto the operand stack.
\item If the token is an operator (\verb'tPlus' or \verb'tTimes'), then
\begin{itemize}
\item if the operator has stronger binding precedence than the one
on top of the operator stack, or if the operator stack is
empty, remove the operator from the input list and push it onto
the operator stack,
\item if the operator has weaker binding precedence than the one on
top of the operator stack, then leave it in the input list and
reduce an expression onto the operand stack.
\end{itemize}
\item If the token is a left paren (\verb'tLparen'), then remove the
operator from the input list and push it onto the operator stack.
\item If the token is a right paren (\verb'tRparen'), then
\begin{itemize}
\item if the operator on top of the operator stack is a left paren
(\verb'tLparen'), the remove the right paren from the input
list and pop the left paren from the operator stack,
\item otherwise reduce an expression onto the operand stack.
\end{itemize}
\item If the input list is empty, then
\begin{itemize}
\item if there is one expression on the operand stack, then return
that expression as the final result,
\item otherwise, reduce an expression onto the operand stack.
\end{itemize}
\end{itemize}
The main idea behind this algorithm is to put off processing an operator
until we know we have parsed both of its operands. This is accomplished by
keeping the operator stack in precedence order, weakest at the bottom to
strongest at the top. Figures~\ref{fig:parse1} and \ref{fig:parse2} show two
examples of its execution, where stacks are written with their tops to the
left. Tokens are represented by characters, and expression trees have the form
$\textsf{op}(x, y)$.
\newcommand{\thr}{\texttt{3}}
\newcommand{\tw}{\texttt{2}}
\newcommand{\pl}{\texttt{+}}
\newcommand{\ti}{\texttt{*}}
\newcommand{\fv}{\texttt{5}}
\begin{figure}[e]
\begin{center}
\begin{tabular}{|r|r|r|l|}
\hline
\multicolumn{1}{|c|}{\textbf{Input}}
& \multicolumn{1}{|c|}{\textbf{Operands}}
& \multicolumn{1}{|c|}{\textbf{Operators}}
& \multicolumn{1}{|c|}{\textbf{Notes}} \\
\hline
\tw, \pl, \thr, \ti, \fv & & & Initial state \\
\hline
\pl, \thr, \ti, \fv & $2$ & & Numbers always get pushed \\
\hline
\thr, \ti, \fv & $2$ & ${+}$ & \\
\hline
\ti, \fv & $3, 2$ & ${+}$ & \\
\hline
\fv & $3, 2$ & ${\times}, {+}$ & ${\times}$ binds stronger than ${+}$, so ${\times}$ pushed \\
\hline
& $5, 3, 2$ & ${\times}, {+}$ & \\
\hline
& ${\times}(3, 5), 2$ & ${+}$ & Input is empty, so reduce an expression \\
\hline
& ${+}(2, {\times}(3, 5))$ & & Final result is on top of operand stack \\
\hline
\end{tabular}
\end{center}
\caption{Parse for the expression $2 + 3 * 5$.}
\label{fig:parse1}
\end{figure}
\begin{figure}[e]
\begin{center}
\begin{tabular}{|r|r|r|l|}
\hline
\multicolumn{1}{|c|}{\textbf{Input}}
& \multicolumn{1}{|c|}{\textbf{Operands}}
& \multicolumn{1}{|c|}{\textbf{Operators}}
& \multicolumn{1}{|c|}{\textbf{Notes}} \\
\hline
\tw, \ti, \thr, \pl, \fv & & & Initial state \\
\hline
\ti, \thr, \pl, \fv & $2$ & & Numbers always get pushed \\
\hline
\thr, \pl, \fv & $2$ & ${\times}$ & \\
\hline
\pl, \fv & $3$, $2$ & ${\times}$ & \\
\hline
\pl, \fv & ${\times}(2, 3)$ & & ${+}$ binds weaker than ${\times}$, so reduce \\
\hline
\fv & ${\times}(2, 3)$ & ${+}$ & Now ${+}$ can be pushed \\
\hline
& $5$, ${\times}(2, 3)$ & ${+}$ & \\
\hline
& ${+}({\times}(2, 3), 5)$ & & Input is empty, so reduce an expression \\
\hline
\end{tabular}
\end{center}
\caption{Parse for the expression $2 * 3 + 5$}.
\label{fig:parse2}
\end{figure}
\begin{task}[10 pts]
Write a predicate \verb'parse(+[token], -exp)' that implements the shunting
yard algorithm. Remember to try to make full use of the expressive power
of logic programming to obtain an elegant program.
\end{task}
You may find GNU Prolog's trace facility useful to debug your program: it
prints each predicate upon entry, exit, and failure. To turn it on, press
\verb'^C' at the prompt and then press \verb't'.
\begin{ectask}[3 extra credit pts]
The algorithm as specified above will succeed on inputs like [ \pl, \thr,
\fv ], [ \tw, \pl, \ti, \thr, \fv ], and [ \verb'(', \verb')', \fv ].
Extend your code to rule out these cases, and describe your solution in
comments.
\end{ectask}
\end{document}