@@ -22,6 +22,7 @@ cabal.project.local~ | |||
.ghc.environment.* | |||
*.exe | |||
*.zip | |||
## Core latex/pdflatex auxiliary files: |
@@ -112,7 +112,7 @@ | |||
%% DEFINITION HELPERS | |||
%%------------------------------------------------------------------------ | |||
\input{defs.tex} | |||
\input{../common/defs.tex} | |||
\pagestyle{fancyplain} |
@@ -0,0 +1,129 @@ | |||
-- CS 839, Spring 2019: Homework 2 | |||
-- Part 1: Append lists (30) | |||
-- Do not change the following line! | |||
{-# OPTIONS -fwarn-incomplete-patterns -fwarn-tabs -fno-warn-type-defaults #-} | |||
-- **Your code should compile without warnings.** | |||
-- The following line makes the compiler treat all warnings as hard errors. | |||
-- When you are done, uncomment it and fix until there are no more errors. | |||
-- (You may ignore unused function warnings.) | |||
{-# OPTIONS -Wall -Werror #-} | |||
-- You might want some functions from these libraries: | |||
-- Data.List | |||
-- | |||
-- In the functional programming style, we usually avoid mutating or updating | |||
-- memory cells, preferring instead pure operations. Accordingly, data | |||
-- structures in functional languages look rather different from the data | |||
-- structures you might be used to. In this assignment, you will write a few | |||
-- purely functional data structures. | |||
-- Concatenating lists with `(++)` is an expensive operation. It is linear in | |||
-- the length of the first argument, which must be "unravelled" and then added | |||
-- back on one at a time. | |||
-- To improve this, let's consider AppendLists. These use higher-order functions | |||
-- so that appending becomes a constant-time operation. The price is that the | |||
-- other list operations are much less efficient. | |||
-- | |||
-- Note that for some of these functions, the best solution is just to convert | |||
-- the append list to a regular list, perform the list operation there, and then | |||
-- convert back to an append list. This is unavoidable for some operations, but | |||
-- try to see if you can perform the operation more efficiently before reaching | |||
-- for regular lists (e.g., for append). | |||
-- The elements of the list will be of type `a`. | |||
newtype AppendList a = AList ([a] -> [a]) | |||
-- The empty list is represented using the identity function, by | |||
-- AList (\x -> x) | |||
empty :: AppendList a | |||
empty = AList (\x -> x) | |||
-- The list [3] is represented as AList (\x -> 3:x) | |||
-- The list [1, 9, 4] will be represented as the function (\x -> 1 : 9: 4 : x). | |||
-- Based on these examples, write a function that takes on argument and returns | |||
-- the AList that corresponds to the list with just that argument as element | |||
singleton :: a -> AppendList a | |||
singleton = undefined | |||
-- Write a function that prepends an element to any appendlist, just like cons | |||
-- (written : ) does for lists | |||
alistCons :: a -> AppendList a -> AppendList a | |||
alistCons = undefined | |||
-- Write foldr and map functions for AppendLists, just like for Lists | |||
alistFoldr :: (a -> b -> b) -> b -> AppendList a -> b | |||
alistFoldr = undefined | |||
-- Try to write this without using fromList | |||
alistMap :: (a -> b) -> AppendList a -> AppendList b | |||
alistMap = undefined | |||
-- Finally, write the function that motivated all of this: append two AppendLists | |||
-- together. | |||
alistAppend :: AppendList a -> AppendList a -> AppendList a | |||
alistAppend = undefined | |||
-- Write a concatenation function which takes a list of AppendLists and turns | |||
-- them into a single AppendList, preserving the order. | |||
alistConcat :: [AppendList a] -> AppendList a | |||
alistConcat = undefined | |||
-- Write a replication function which makes an AppendList by repeating the given | |||
-- element for a given number of times (possibly zero times). | |||
alistReplicate :: Int -> a -> AppendList a | |||
alistReplicate = undefined | |||
-- Write a function that converts an append list to the regular list that it | |||
-- represents | |||
toList :: AppendList a -> [a] | |||
toList = undefined | |||
-- Write a function that does the opposite, converting a normal list into an | |||
-- AppendList | |||
fromList :: [a] -> AppendList a | |||
fromList = undefined | |||
-- Write a function that computes the head of an AppendList (your function may | |||
-- fail when the AppendList is empty). | |||
alistHead :: AppendList a -> a | |||
alistHead = undefined | |||
-- Write a function that computes the tail of an AppendList. | |||
alistTail :: AppendList a -> AppendList a | |||
alistTail = undefined | |||
-- Basic tests: you should do more testing! | |||
checkEq :: (Eq a, Show a) => a -> a -> String | |||
checkEq expected got | |||
| expected == got = "PASS" | |||
| otherwise = "FAIL Expected: " ++ (show expected) ++ " Got: " ++ (show got) | |||
main :: IO () | |||
main = let myList = [1, 3, 5, 7, 9] :: [Int] | |||
myList' = [8, 6, 4, 2, 0] :: [Int] in | |||
do putStr "toList/fromList: " | |||
putStrLn $ checkEq myList (toList . fromList $ myList) | |||
putStr "alistHead: " | |||
putStrLn $ checkEq (head myList') (alistHead . fromList $ myList') | |||
putStr "alistTail: " | |||
putStrLn $ checkEq (tail myList) (toList . alistTail . fromList $ myList) | |||
putStr "alistFoldr: " | |||
putStrLn $ checkEq (foldr (+) 0 myList) (alistFoldr (+) 0 $ fromList myList) | |||
putStr "alistMap: " | |||
putStrLn $ checkEq (map (+ 1) myList') (toList (alistMap (+ 1) $ fromList myList')) | |||
putStr "alistAppend: " | |||
putStrLn $ checkEq (myList ++ myList') (toList $ alistAppend (fromList myList) (fromList myList')) | |||
putStr "alistConcat: " | |||
putStrLn $ checkEq (concat [myList', myList]) (toList $ alistConcat [(fromList myList'), (fromList myList)]) | |||
putStr "alistReplicate: " | |||
putStrLn $ checkEq ([42, 42, 42, 42, 42] :: [Int]) $ toList (alistReplicate 5 42) |
@@ -0,0 +1,137 @@ | |||
-- CS 839, Spring 2019: Homework 2 | |||
-- Part 2: List zippers (30) | |||
-- Do not change the following line! | |||
{-# OPTIONS -fwarn-incomplete-patterns -fwarn-tabs -fno-warn-type-defaults #-} | |||
-- **Your code should compile without warnings.** | |||
-- The following line makes the compiler treat all warnings as hard errors. | |||
-- When you are done, uncomment it and fix until there are no more errors. | |||
-- (You may ignore unused function warnings.) | |||
-- {-# OPTIONS -Wall -Werror #-} | |||
-- You might want some functions from these libraries: | |||
-- Data.List | |||
-- A zipper takes a plain datastructure---a list, a tree, etc.---and adds an | |||
-- extra bit of information describing a position within the datastructure. This | |||
-- position behaves much like a cursor: we can move the cursor, lookup the value | |||
-- at the cursor, or update/delete the value at the cursor. | |||
-- We can represent a position in a non-empty plain list with three parts: the | |||
-- list of elements before the position, the element at the position, and the | |||
-- list of elements after the position. Otherwise, the zipper may be over an | |||
-- empty list. The elements of the list will be of type `a`. | |||
data ListZipper a = | |||
LZEmpty | |||
| LZItems [a] -- list of items before | |||
a -- current item | |||
[a] -- list of items after | |||
-- One detail about Haskell lists is that they support efficient operations at | |||
-- only one end, the start (head) of the list. Working at the tail end | |||
-- requires traversing the whole list, an expensive operation. | |||
-- | |||
-- To make the zipper operations easier to write and more efficient, we | |||
-- store the list of items before in *reversed* order. For instance, if the | |||
-- underlying list is [1, 2, 3, 4, 5] and the current position is 3, then the | |||
-- zipper should look like | |||
-- | |||
-- LzItems [2, 1] 3 [4, 5] | |||
-- | |||
-- Keep this invariant in mind, and make sure it is maintained through all of | |||
-- the operations. | |||
-- | |||
-- Write a function to convert a regular list into a zipper, with the initial | |||
-- position set to the first element (if the list is non-empty). | |||
zipOfList :: [a] -> ListZipper a | |||
zipOfList = undefined | |||
-- Write a function to convert a zipper back into a regular list. | |||
zipToList :: ListZipper a -> [a] | |||
zipToList = undefined | |||
-- Write a function to get the current item. Note that the function returns | |||
-- `Maybe a`. Your function should return `Nothing` if the zipper is empty. | |||
getCurItem :: ListZipper a -> Maybe a | |||
getCurItem = undefined | |||
-- Write functions to move the position left or right in the zipper. Since we | |||
-- cannot actually change the input zipper in a pure functional language, your | |||
-- function will return a zipper with the updated position. Remember to keep | |||
-- track of the elements in the zipper: moving left or right should not change | |||
-- the elements in the underlying list. | |||
-- | |||
-- If the input zipper is empty, or the move is not valid (trying to move left | |||
-- in the first position, or trying to move right in the last position), return | |||
-- the original zipper with no change. Your function should be total---it should | |||
-- never raise an error! | |||
goLeftL :: ListZipper a -> ListZipper a | |||
goLeftL = undefined | |||
goRightL :: ListZipper a -> ListZipper a | |||
goRightL = undefined | |||
-- Write a function to replace the item at the current position in the zipper | |||
-- with a new element. If the input zipper is empty, return the input zipper | |||
-- unchanged. Again, your function should never raise an error! | |||
updateItem :: a -> ListZipper a -> ListZipper a | |||
updateItem = undefined | |||
-- Write a function to insert a new element into the zipper *after* the current | |||
-- position. The cursor should move to the new item after the insertion. Your | |||
-- function should behave correctly for all zippers, including the empty zipper. | |||
insertItem :: a -> ListZipper a -> ListZipper a | |||
insertItem = undefined | |||
-- Write a function to delete the current element from the zipper. The cursor | |||
-- should move to the item before the deleted position, or after the first | |||
-- position when deleting the first item. Again, your function should behave | |||
-- correctly for all zippers, including the empty zipper. (Deleting from the | |||
-- empty zipper should just return the empty zipper.) | |||
deleteItem :: ListZipper a -> ListZipper a | |||
deleteItem = undefined | |||
-- This main function forms lists of strings. You enter commands to move around | |||
-- the zipper and modify the list using it. If you want to start from a | |||
-- different zipper, change the definition below. | |||
initZip :: ListZipper String | |||
initZip = LZEmpty | |||
main :: IO () | |||
main = | |||
aux initZip | |||
where headMay xs = case xs of | |||
(x:_) -> Just x | |||
[] -> Nothing | |||
isSpace c = c `elem` "\t \n\r\v\f" | |||
aux zipp = do | |||
putStr "-- Current list: " | |||
putStr $ show (zipToList zipp) | |||
case getCurItem zipp of | |||
Just el -> do putStr " at element: " | |||
print el | |||
Nothing -> putStrLn "" | |||
putStrLn ("-- Enter an operation on the list: " ++ | |||
"(l)eft, (r)ight, (e)dit, (i)nsert, or (d)elete:") | |||
input <- getLine | |||
case headMay input of | |||
Just 'l' -> aux (goLeftL zipp) | |||
Just 'r' -> aux (goRightL zipp) | |||
Just 'e' -> let w2 = dropWhile isSpace (tail input) | |||
in aux (updateItem w2 zipp) | |||
Just 'i' -> let w2 = dropWhile isSpace (tail input) | |||
in aux (insertItem w2 zipp) | |||
Just 'd' -> aux (deleteItem zipp) | |||
_ -> do putStrLn "!! Error: unrecognized command" | |||
aux zipp | |||
@@ -0,0 +1,183 @@ | |||
-- CS 839, Spring 2019: Homework 2 | |||
-- Part 3: Tree zippers (40) | |||
-- Do not change the following line! | |||
{-# OPTIONS -fwarn-incomplete-patterns -fwarn-tabs -fno-warn-type-defaults #-} | |||
-- **Your code should compile without warnings.** | |||
-- The following line makes the compiler treat all warnings as hard errors. | |||
-- When you are done, uncomment it and fix until there are no more errors. | |||
-- (You may ignore unused function warnings.) | |||
{-# OPTIONS -Wall -Werror #-} | |||
-- You might want some functions from these libraries: | |||
-- Data.List | |||
-- We'll now extend zippers to work for trees. First, we'll set up a datatype | |||
-- for plain trees. Trees will either consist of a leaf with a piece of data of | |||
-- type `a`, or a list of trees. Complete the following datatype declaration, | |||
-- replacing () with the correct types. | |||
data Tree a = | |||
Leaf () | |||
| Node () | |||
-- Just like before, we want to represent a (1) position and an (2) item with | |||
-- our zipper. Both of these pieces are more complicated for trees. Let's start | |||
-- with representing positions in a tree. Positions may be either leaf nodes or | |||
-- internal nodes. We will represent positions recursively: a position of a node | |||
-- is either the root of a tree, or it is the position of its parent node along | |||
-- with two lists describing the node's siblings (child nodes of the same | |||
-- parent): the sibling trees before the current node, and sibling trees after | |||
-- the current node. | |||
-- | |||
-- Fill out the following datatype declaration for the type of paths, replacing | |||
-- (or removing) (). | |||
data Path a = | |||
Top | |||
| Loc () () () | |||
-- Just like we did for list zippers, we will store the list of siblings before | |||
-- the current position in *reverse* order, and the list of siblings after the | |||
-- current position in *normal* order. Keep this invariant in mind and make sure | |||
-- that it is preserved in all the operations. | |||
-- Since the position may be an internal node, the item at a position may be a | |||
-- tree. By combining these two pieces of data, we are ready to define the type | |||
-- of tree zippers. | |||
data TreeZipper a = TZ { getPath :: Path a, getItem :: Tree a } | |||
-- Write a function to convert a regular tree into a zipper, with the initial | |||
-- position set to the root. | |||
zipOfTree :: Tree a -> TreeZipper a | |||
zipOfTree = undefined | |||
-- Write a function to convert a zipper back into a regular tree. | |||
-- (Hint: you'll want to recurse, calling zipToTree on a smaller path.) | |||
zipToTree :: TreeZipper a -> Tree a | |||
zipToTree = undefined | |||
-- Write a function to get the subtree at the current position. | |||
getCurTree :: TreeZipper a -> Tree a | |||
getCurTree = undefined | |||
-- For trees, we can move the position in more directions. A left/right move | |||
-- corresponds to switching to one of the siblings of the current position, | |||
-- while an up/down move corresponds to moving to the parent or moving to the | |||
-- first child of the current position. | |||
-- | |||
-- If the move is not valid (trying to move left at the first sibling, or trying | |||
-- to move right in the last sibling, etc.), return the original zipper with no | |||
-- change. Your function should be total---it should never raise an error! | |||
goLeftT :: TreeZipper a -> TreeZipper a | |||
goLeftT = undefined | |||
goRightT :: TreeZipper a -> TreeZipper a | |||
goRightT = undefined | |||
goUpT :: TreeZipper a -> TreeZipper a | |||
goUpT = undefined | |||
goDownT :: TreeZipper a -> TreeZipper a | |||
goDownT = undefined | |||
-- Write a function to replace the tree at the current position in the zipper | |||
-- with a new subtree. Again, your function should never raise an error! | |||
updateTree :: Tree a -> TreeZipper a -> TreeZipper a | |||
updateTree = undefined | |||
-- Write a function to insert a new subtree into the zipper. There are three | |||
-- possible places to insert: left (as a new sibling before the current | |||
-- position), right (as a new sibling after the current position), and down (as | |||
-- a new first/left-most child of the current position). The cursor should end | |||
-- up on the newly inserted item. If the insertion is invalid (inserting | |||
-- left/right at the root), return the original zipper. Again, your function | |||
-- should never raise an error! | |||
insertLeftT :: Tree a -> TreeZipper a -> TreeZipper a | |||
insertLeftT = undefined | |||
insertRightT :: Tree a -> TreeZipper a -> TreeZipper a | |||
insertRightT = undefined | |||
insertDownT :: Tree a -> TreeZipper a -> TreeZipper a | |||
insertDownT = undefined | |||
-- Write a function to delete the whole subtree at the current position from the | |||
-- zipper. The final position of the zipper should be (in decreasing priority) | |||
-- the next sibling on the right, or the previous sibling on the left, or the | |||
-- parent node if there are no other siblings. | |||
-- | |||
-- Again, your function should behave correctly for all zippers; deleting the | |||
-- root node should return the original zipper. | |||
deleteT :: TreeZipper a -> TreeZipper a | |||
deleteT = undefined | |||
-- This main function forms lists of strings. You enter commands to move around | |||
-- the zipper and modify the list using it. If you want to start from a | |||
-- different zipper, change the definition below. | |||
-- (Note: the following will not compile until you define the TreeZipper type.) | |||
initZip :: TreeZipper Integer | |||
initZip = undefined | |||
main :: IO () | |||
main = | |||
aux initZip | |||
where headMay xs = case xs of | |||
(x:_) -> Just x | |||
[] -> Nothing | |||
isDigit c = c `elem` "0123456789" | |||
showTree t = case t of | |||
Leaf a -> show a | |||
Node children -> "(" ++ unwords ch_strs ++ ")" | |||
where ch_strs = map showTree children | |||
readTree word = case word of | |||
"()" -> Right (Node []) | |||
_ -> if all isDigit word | |||
then Right (Leaf (read word)) | |||
else Left ("!! Error: Second argument must " | |||
++ "be a number or ()") | |||
onSnd wrds = case tail wrds of | |||
[] -> Left "!! Error: need second argument" | |||
(sec:_) -> readTree sec | |||
aux :: TreeZipper Integer -> IO () | |||
aux zipp = do | |||
putStr "-- Current Tree: " | |||
putStr $ showTree (zipToTree zipp) | |||
putStr " at subtree: " | |||
print (showTree (getCurTree zipp)) | |||
putStrLn ("-- Enter an operation on the list:\n" ++ | |||
"-- (l)eft, (r)ight, (u)p, (d)own,\n-- (e)dit, " ++ | |||
"(il) insert left, (ir) insert right, " ++ | |||
"(id) insert down,\n-- or (del)ete:") | |||
input <- getLine | |||
let wrds = words input | |||
case headMay wrds of | |||
Just "l" -> aux (goLeftT zipp) | |||
Just "r" -> aux (goRightT zipp) | |||
Just "u" -> aux (goUpT zipp) | |||
Just "d" -> aux (goDownT zipp) | |||
Just "e" -> case onSnd wrds of | |||
Left msg -> putStrLn msg >> aux zipp | |||
Right tr -> aux (updateTree tr zipp) | |||
Just "il" -> case onSnd wrds of | |||
Left msg -> putStrLn msg >> aux zipp | |||
Right tr -> aux (insertLeftT tr zipp) | |||
Just "ir" -> case onSnd wrds of | |||
Right tr -> aux (insertRightT tr zipp) | |||
Left str -> putStrLn str >> aux zipp | |||
Just "id" -> case onSnd wrds of | |||
Right tr -> aux (insertDownT tr zipp) | |||
Left str -> putStrLn str >> aux zipp | |||
Just "del" -> aux (deleteT zipp) | |||
_ -> putStrLn "!! Error: unrecognized command" | |||
>> aux zipp |
@@ -0,0 +1,425 @@ | |||
\documentclass[a4paper]{article} | |||
\usepackage{geometry} | |||
\usepackage{graphicx} | |||
\usepackage{natbib} | |||
\usepackage{amsmath} | |||
\usepackage{amssymb} | |||
\usepackage{amsthm} | |||
\usepackage{paralist} | |||
\usepackage{epstopdf} | |||
\usepackage{tabularx} | |||
\usepackage{longtable} | |||
\usepackage{multirow} | |||
\usepackage{multicol} | |||
\usepackage[hidelinks]{hyperref} | |||
\usepackage{fancyvrb} | |||
\usepackage{algorithm} | |||
\usepackage{algorithmic} | |||
\usepackage{float} | |||
\usepackage{listings} | |||
\usepackage[svgname]{xcolor} | |||
\usepackage{enumerate} | |||
\usepackage{array} | |||
\usepackage{times} | |||
\usepackage{url} | |||
\usepackage{fancyhdr} | |||
\usepackage{comment} | |||
\usepackage{environ} | |||
\usepackage{times} | |||
\usepackage{textcomp} | |||
\usepackage{caption} | |||
\urlstyle{rm} | |||
\setlength\parindent{0pt} % Removes all indentation from paragraphs | |||
\theoremstyle{definition} | |||
\newtheorem{definition}{Definition}[] | |||
\newtheorem{conjecture}{Conjecture}[] | |||
\newtheorem{example}{Example}[] | |||
\newtheorem{theorem}{Theorem}[] | |||
\newtheorem{lemma}{Lemma} | |||
\newtheorem{proposition}{Proposition} | |||
\newtheorem{corollary}{Corollary} | |||
\floatname{algorithm}{Procedure} | |||
\renewcommand{\algorithmicrequire}{\textbf{Input:}} | |||
\renewcommand{\algorithmicensure}{\textbf{Output:}} | |||
\newcommand{\abs}[1]{\lvert#1\rvert} | |||
\newcommand{\norm}[1]{\lVert#1\rVert} | |||
\newcommand{\RR}{\mathbb{R}} | |||
\newcommand{\CC}{\mathbb{C}} | |||
\newcommand{\Nat}{\mathbb{N}} | |||
\newcommand{\br}[1]{\{#1\}} | |||
\DeclareMathOperator*{\argmin}{arg\,min} | |||
\DeclareMathOperator*{\argmax}{arg\,max} | |||
\renewcommand{\qedsymbol}{$\blacksquare$} | |||
\definecolor{dkgreen}{rgb}{0,0.6,0} | |||
\definecolor{gray}{rgb}{0.5,0.5,0.5} | |||
\definecolor{mauve}{rgb}{0.58,0,0.82} | |||
\newcommand{\Var}{\mathrm{Var}} | |||
\newcommand{\Cov}{\mathrm{Cov}} | |||
\newcommand{\vc}[1]{\boldsymbol{#1}} | |||
\newcommand{\xv}{\vc{x}} | |||
\newcommand{\Sigmav}{\vc{\Sigma}} | |||
\newcommand{\alphav}{\vc{\alpha}} | |||
\newcommand{\muv}{\vc{\mu}} | |||
\newcommand{\red}[1]{\textcolor{red}{#1}} | |||
\def\x{\mathbf x} | |||
\def\y{\mathbf y} | |||
\def\w{\mathbf w} | |||
\def\v{\mathbf v} | |||
\def\E{\mathbb E} | |||
\def\V{\mathbb V} | |||
% TO SHOW SOLUTIONS, include following (else comment out): | |||
\newenvironment{soln}{ | |||
\leavevmode\color{blue}\ignorespaces | |||
}{} | |||
\hypersetup{ | |||
% colorlinks, | |||
linkcolor={red!50!black}, | |||
citecolor={blue!50!black}, | |||
urlcolor={blue!80!black} | |||
} | |||
\geometry{ | |||
top=1in, % <-- you want to adjust this | |||
inner=1in, | |||
outer=1in, | |||
bottom=1in, | |||
headheight=3em, % <-- and this | |||
headsep=2em, % <-- and this | |||
footskip=3em, | |||
} | |||
\lstset{ | |||
basicstyle=\ttfamily, | |||
columns=fullflexible, | |||
breaklines=true, | |||
keepspaces=true, | |||
postbreak=\raisebox{0ex}[0ex][0ex]{\color{red}$\hookrightarrow$\space} | |||
} | |||
%%------------------------------------------------------------------------ | |||
%% DEFINITION HELPERS | |||
%%------------------------------------------------------------------------ | |||
\input{../common/defs.tex} | |||
\pagestyle{fancyplain} | |||
\lhead{\fancyplain{}{Homework 1: Written Assignment}} | |||
\rhead{\fancyplain{}{CS 538 Theory and Design of Programming Languages}} | |||
\cfoot{\thepage} | |||
\title{\textsc{CS 538, Spring 2019}} % Title | |||
%%% NOTE: Replace 'NAME HERE' etc., and delete any "\red{}" wrappers (so it won't show up as red) | |||
\author{ | |||
Everette Rong \\ | |||
yrong9@wisc.edu \\ | |||
rong@cs.wisc.edu \\ | |||
% \red{$>>$ID HERE$<<$}\\ | |||
} | |||
\date{} | |||
\begin{document} | |||
\maketitle | |||
\begin{center} | |||
\Huge | |||
HW1: Written Assignment 1 | |||
\end{center} | |||
\section{Calculator language: Syntax (10)} | |||
Translate this English description of the language into a grammar. | |||
\begin{soln} | |||
\begin{lstlisting} | |||
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | |||
num = digit { digit } | |||
bool = "tt" | "ff" (* boolean constants *) | |||
int = [ "-" ] num (* positive/negative int *) | |||
ae = int (* integer constants *) | |||
| ae "+" ae (* addition *) | |||
| ae "*" ae (* multiplication *) | |||
| "if" be "then" ae "else" ae (* if-then-else *) | |||
be = bool (* boolean constants *) | |||
| be "&&" be (* logical and *) | |||
| be "||" be (* logical or *) | |||
| ae "==" ae (* equals *) | |||
| ae "<" ae (* less than *) | |||
\end{lstlisting} | |||
\end{soln} | |||
\section{Calculator language: Operational semantics (10)} | |||
\begin{enumerate} | |||
\item Give a small-step operational semantics for our calculator language. | |||
\begin{soln} | |||
Preliminaries: | |||
\begin{itemize} | |||
\item Both addition and multiplication will be calculated from left to right. | |||
\item $add$ and $times$ mean the actual mathematical calculation of $+$ and $*$. | |||
\item $\land$ and $\lor$ mean the actual logical operation of $and$ and $or$. | |||
\item $-$ on the top means there is no step. | |||
\item $e_i$ means the corresponding expression (int ($ae$) or bool ($be$)) in those steps. | |||
\item $n_i$ represents an int constant, and $b_i$ represents a bool constant. | |||
\end{itemize} | |||
If-then-else (\textit{lazy-if}): | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{be_1}{be_1'}} | |||
{\rstep{(\text{if } be_1 \text{ then } ae_1 \text{ else } ae_2)}{(\text{if } be_1' \text{ then } ae_1 \text{ else } ae_2)}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{b_1 == tt} | |||
{\rstep{(\text{if } be_1 \text{ then } ae_1 \text{ else } ae_2)}{ae_1}} | |||
\\ | |||
\\ | |||
\infr[(3)] | |||
{b_1 == ff} | |||
{\rstep{(\text{if } be_1 \text{ then } ae_1 \text{ else } ae_2)}{ae_2}} | |||
\end{array} | |||
\] | |||
Integer Addition: | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{e_1}{e_1'}} | |||
{\rstep{e_1 + e_2}{e_1' + e_2}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{e_2}{e_2'}} | |||
{\rstep{n_1 + e_2}{n_1 + e_2'}} | |||
\\ | |||
\\ | |||
\infr[$n_3 = add(n_1, n_2)$ (3)] | |||
{-} | |||
{\rstep{n_1 + n_2}{n_3}} | |||
\end{array} | |||
\] | |||
Similarly, Integer Multiplication: | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{e_1}{e_1'}} | |||
{\rstep{e_1 * e_2}{e_1' * e_2}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{e_2}{e_2'}} | |||
{\rstep{n_1 * e_2}{n_1 * e_2'}} | |||
\\ | |||
\\ | |||
\infr[$n_3 = times(n_1, n_2)$ (3)] | |||
{-} | |||
{\rstep{n_1 * n_2}{n_3}} | |||
\end{array} | |||
\] | |||
Boolean Logical And: | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{e_1}{e_1'}} | |||
{\rstep{e_1 \ \&\&\ e_2}{e_1'\ \&\&\ e_2}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{e_2}{e_2'}} | |||
{\rstep{e_1\ \&\&\ e_2}{e_1\ \&\&\ e_2'}} | |||
\\ | |||
\\ | |||
\infr[$b_3 = (b_1 \land b_2)$ (3)] | |||
{-} | |||
{\rstep{b_1\ \&\&\ b_2}{b_3}} | |||
\end{array} | |||
\] | |||
Boolean Logical Or: | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{e_1}{e_1'}} | |||
{\rstep{e_1\ ||\ e_2}{e_1'\ ||\ e_2}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{e_2}{e_2'}} | |||
{\rstep{e_1\ ||\ e_2}{e_1\ ||\ e_2'}} | |||
\\ | |||
\\ | |||
\infr[$b_3 = (b_1 \lor b_2)$ (3)] | |||
{-} | |||
{\rstep{b_1\ ||\ b_2}{b_3}} | |||
\end{array} | |||
\] | |||
Integer Equal: | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{e_1}{e_1'}} | |||
{\rstep{e_1 == e_2}{e_1' == e_2}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{e_2}{e_2'}} | |||
{\rstep{e_1 == e_2}{e_1 == e_2'}} | |||
\\ | |||
\\ | |||
\infr[$b_3 = (b_1 == b_2)$ (3)] | |||
{-} | |||
{\rstep{b_1 == b_2}{b_3}} | |||
\end{array} | |||
\] | |||
Integer Less than: | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{e_1}{e_1'}} | |||
{\rstep{e_1 < e_2}{e_1' < e_2}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{e_2}{e_2'}} | |||
{\rstep{e_1 < e_2}{e_1 < e_2'}} | |||
\\ | |||
\\ | |||
\infr[$b_3 = (b_1 < b_2)$ (3)] | |||
{-} | |||
{\rstep{b_1 < b_2}{b_3}} | |||
\end{array} | |||
\] | |||
\end{soln} | |||
\item | |||
\begin{itemize} | |||
\item Give an alternative operational semantics for if-then-else that evaluates both bodies before evaluating | |||
the guard | |||
\begin{soln} | |||
If-then-else (\textit{eager-if}): | |||
\[ | |||
\begin{array}{rcll} | |||
\infr[(1)] | |||
{\rstep{ae_1}{ae_1'}} | |||
{\rstep{(\text{if } be_1 \text{ then } ae_1 \text{ else } ae_2)}{(\text{if } be_1 \text{ then } ae_1' \text{ else } ae_2)}} | |||
\\ | |||
\\ | |||
\infr[(2)] | |||
{\rstep{ae_2}{ae_2'}} | |||
{\rstep{(\text{if } be_1 \text{ then } ae_1 \text{ else } ae_2)}{(\text{if } be_1 \text{ then } ae_1 \text{ else } ae_2')}} | |||
\\ | |||
\\ | |||
\infr[(3)] | |||
{b_1 == tt} | |||
{\rstep{(\text{if } be_1 \text{ then } n_1 \text{ else } n_2)}{n_1}} | |||
\\ | |||
\\ | |||
\infr[(4)] | |||
{b_1 == ff} | |||
{\rstep{(\text{if } be_1 \text{ then } n_1 \text{ else } n_2)}{n_2}} | |||
\end{array} | |||
\] | |||
\end{soln} | |||
\item What are some reasons to prefer \textit{lazy-if} over \textit{eager-if} ? | |||
\begin{soln} | |||
In \textit{lazy-if}, we don't need to calculate both expression before we can get the final result. We always evaluate one bool condition, and one arithmatic expression. | |||
\textit{Eager-if} on the other hand, requires us to always evaluate one bool condition and two arithmatic expressions. \\ | |||
Also, \textit{lazy-if} won't stuck in an expression that will never be reached, while \textit{eager-if} would always stuck if any expression is problematic. | |||
\end{soln} | |||
\end{itemize} | |||
\end{enumerate} | |||
\section{Lambda calculus (5)} | |||
Evaluate each of the following programs until it reaches a value, or returns to the original program. Show | |||
each step in the evaluation. | |||
\begin{enumerate} | |||
\item $(\lambda f \cdot \lambda x \cdot f x) (\lambda x \cdot x + 1) 5$\\ | |||
\begin{soln} | |||
$= (\lambda x \cdot (\lambda a \cdot a+1)\ x)\ 5$ \\ | |||
$= (\lambda x \cdot x + 1)\ 5 $ \\ | |||
$= 5+1$ \\ | |||
$= 6$ | |||
\end{soln} | |||
\item $(\lambda f \cdot \lambda x \cdot \lambda y \cdot f\ y\ x)\ (\lambda x \cdot \lambda y \cdot x-y)\ 5\ 3$ \\ | |||
\begin{soln} | |||
$=(\lambda x \cdot \lambda y \cdot (\lambda a \cdot \lambda b \cdot a-b)\ y\ x)\ 5\ 3$ \\ | |||
$=(\lambda y \cdot (\lambda x \cdot \lambda a \cdot x - a)\ y\ 5)\ 3$ \\ | |||
$=(\lambda x \cdot \lambda y \cdot x - y)\ 3\ 5$ \\ | |||
$=3 - 5$ \\ | |||
$=-2$ | |||
\end{soln} | |||
\item $(\lambda x \cdot x\ x)\ (\lambda x \cdot x\ x)$ \\ | |||
\begin{soln} | |||
$=(\lambda x \cdot x\ x)\ (\lambda x \cdot x\ x)$ \\ | |||
$=(\lambda x \cdot x\ x)\ (\lambda x \cdot x\ x)$ \\ | |||
$=...$ | |||
\end{soln} | |||
\end{enumerate} | |||
\section{Recursion (5)} | |||
Use the program construct $fix f. e$ we introduced to write a lambda calculus function that takes in a natural | |||
number $n$ and returns the $n-th$ Fibonacci number $fib(n)$. | |||
\begin{soln} | |||
$$ | |||
fib = fix\ f.\ \lambda n.\ \text{ if } n = 1 \text{ then } 1 \text{ else } (\text{ if } n = 2 \text{ then } 1 \text{ else } f(n-1)+f(n-2)) | |||
$$ | |||
\centering \textbf{evaluating fib(3)} | |||
$\rightarrow fix\ f.\ \lambda n.\ \text{ if } n = 1 \text{ then } 1 \text{ else } (\text{ if } n = 2 \text{ then } 1 \text{ else } f(n-1)+f(n-2))\ 3$ \\ | |||
$\rightarrow \text{ if } 3 = 1 \text{ then } 1 \text{ else } (\text{ if } 3 = 2 \text{ then } 1 \text{ else } f(3-1)+f(3-2))$ \\ | |||
$f(2) \rightarrow \text{ if } 2 = 1 \text{ then } 1 \text{ else } (\text{ if } 2 = 2 \text{ then } 1 \text{ else } f(2-1)+f(2-2)) \rightarrow 1$ \\ | |||
$f(1) \rightarrow \text{ if } 1 = 1 \text{ then } 1 \text{ else } (\text{ if } 1 = 2 \text{ then } 1 \text{ else } f(1-1)+f(1-2)) \rightarrow 1$ \\ | |||
$f(3) = f(2) + f(1) = 1+1 = 2$ | |||
\end{soln} | |||
\clearpage % do not erase this! | |||
\bibliographystyle{apalike} | |||
%---------------------------------------------------------------------------------------- | |||
\end{document} |