422 lines
16 KiB
TeX
422 lines
16 KiB
TeX
%! TeX program = lualatex
|
|
\RequirePackage[l2tabu,orthodox]{nag}
|
|
\DocumentMetadata{lang=en-US}
|
|
\documentclass[a4paper]{scrartcl}
|
|
\usepackage{geometry}
|
|
%\usepackage{tikz}
|
|
%\usepackage{tikz-uml}
|
|
\usepackage{hyperref}
|
|
%\usepackage{caption}
|
|
\usepackage{bookmark}
|
|
\usepackage{fontspec}
|
|
\usepackage{microtype}
|
|
\usepackage{minted}
|
|
\hypersetup{
|
|
colorlinks=true,
|
|
urlcolor=blue
|
|
}
|
|
|
|
% Math packages
|
|
%\usepackage{amsmath}
|
|
%\usepackage{mathtools}
|
|
%\usepackage{amsthm}
|
|
%\usepackage{thmtools}
|
|
%\usepackage{lualatex-math}
|
|
|
|
% Unicode Math
|
|
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket},math-style=upright]{unicode-math}
|
|
\usepackage{newcomputermodern}
|
|
|
|
% Use Euler Math fonts and Concrete Roman
|
|
%\usepackage{euler-math}
|
|
%\setmainfont{cmunorm.otf}[
|
|
% BoldFont=cmunobx.otf,
|
|
% ItalicFont=cmunoti.otf,
|
|
% BoldItalicFont=cmunobi.otf
|
|
%]
|
|
\setmonofont{0xProto}[Scale=MatchLowercase]
|
|
|
|
\newcommand*{\figref}[2][]{%
|
|
\hyperref[{fig:#2}]{%
|
|
Figure~\ref*{fig:#2}%
|
|
\ifx\\#1\\%
|
|
\else
|
|
\,#1%
|
|
\fi
|
|
}%
|
|
}
|
|
|
|
%\DeclarePairedDelimiter\ceil{\lceil}{\rceil}
|
|
%\DeclarePairedDelimiter\floor{\lfloor}{\rfloor}
|
|
|
|
%\declaretheorem[within=chapter]{definition}
|
|
%\declaretheorem[sibling=definition]{theorem}
|
|
%\declaretheorem[sibling=definition]{corollary}
|
|
%\declaretheorem[sibling=definition]{principle}
|
|
|
|
\usepackage{polyglossia}
|
|
%\usepackage[backend=biber]{biblatex}
|
|
|
|
\setdefaultlanguage[variant=american,ordinalmonthday=true]{english}
|
|
|
|
\day=15
|
|
\month=3
|
|
\year=2024
|
|
|
|
\title{Programming Assignment 2}
|
|
\subtitle{CS-420 Spring 2024}
|
|
\author{Juan Pablo Zendejas}
|
|
\date{\today}
|
|
|
|
\begin{document}
|
|
|
|
\maketitle
|
|
%\listoftheorems[ignoreall,onlynamed={theorem,corollary,principle}]
|
|
%\listoftheorems[ignoreall,onlynamed={definition},title={List of Definitions}]
|
|
%\tableofcontents
|
|
For this programming assignment, I was tasked with creating a variety of
|
|
functions for different purposes to practice functional programming
|
|
concepts like fold/reduce, recursion, and function composition. In all,
|
|
I managed to get a full score on the autograder and I felt that I had
|
|
created acceptable solutions that are clean and easy to understand.
|
|
|
|
While I didn't work with anyone specifically on this project, I did end
|
|
up searching for help online when I felt I had hit a wall. For example,
|
|
I wanted to use only one \texttt{foldr} call on my \texttt{sepConcat}
|
|
function and I was having trouble creating a lambda function that would
|
|
work. I looked at some
|
|
StackOverflow\footnote{\url{https://stackoverflow.com/a/44101237}} answers
|
|
for some guidance and I
|
|
found I was close, but I just had to re-arrange my use of the \texttt{++}
|
|
operator to follow the ordering of the \texttt{foldr}. I also found out
|
|
about the trick of using \texttt{zip [0..] xs} to get the index of each
|
|
element of a list, which was useful for the final problem.
|
|
|
|
Now, we will look at each function and my solution.
|
|
\section{Part A: Basic Haskell Prelude}
|
|
These functions are simple implementations using basic Haskell prelude
|
|
functions.
|
|
|
|
\subsection{myMod}
|
|
The idea is fairly simple. Use \texttt{div} to perform integer division
|
|
of x and y. Then, we can multiply it by y and subtract that from x to
|
|
get the remainder of the division, which is the modulus operator.
|
|
\begin{minted}[frame=single]{haskell}
|
|
myMod :: Int -> Int -> Int
|
|
myMod x y = x - (y * div x y)
|
|
\end{minted}
|
|
|
|
\subsection{toDigit}
|
|
This function uses recursion. If the input is \le 0, then we just return
|
|
an empty list. The recursive step then splits the integer into the first
|
|
digit using \texttt{myMod}, and then the rest of the number using
|
|
\texttt{div}. Finally, the digit is appended to the end and toDigit is
|
|
called recursively to prepend its result.
|
|
\begin{minted}[frame=single]{haskell}
|
|
toDigit :: Int -> [Int]
|
|
toDigit n | n <= 0 = []
|
|
| otherwise = toDigit (div n 10) ++ [myMod n 10]
|
|
\end{minted}
|
|
|
|
\subsection{reverseList}
|
|
Another recursive function. To reverse the list, I use a pattern
|
|
matching to split the first element of the list out. Then, just prepend
|
|
the reverse of the rest of the list to the first element.
|
|
\begin{minted}[frame=single]{haskell}
|
|
reverseList :: [a] -> [a]
|
|
reverseList [] = []
|
|
reverseList (x:xs) = reverseList xs ++ [x]
|
|
\end{minted}
|
|
|
|
\subsection{sumList}
|
|
Follows a similar strategy. Pattern-matching to split the first element
|
|
off, then add it to the sum of the rest of the list. This recursive call
|
|
is ended by a base case of 0 for an empty list.
|
|
\begin{minted}[frame=single]{haskell}
|
|
sumList :: [Int] -> Int
|
|
sumList [] = 0
|
|
sumList (x:xs) = sumList xs + x
|
|
\end{minted}
|
|
|
|
\subsection{toDigitRev}
|
|
Just a function composition.
|
|
\begin{minted}[frame=single]{haskell}
|
|
toDigitRev :: Int -> [Int]
|
|
toDigitRev n = reverseList (toDigit n)
|
|
\end{minted}
|
|
|
|
\section{Part B: Folding Functions}
|
|
These functions use the \texttt{foldr} prelude function to perform
|
|
operations on a list.
|
|
|
|
\subsection{myDouble}
|
|
The idea was to use \texttt{foldr} with a list of the parameter twice,
|
|
so foldr could add them together.
|
|
\begin{minted}[frame=single]{haskell}
|
|
myDouble :: Int -> Int
|
|
myDouble n = foldr (+) 0 [n,n]
|
|
\end{minted}
|
|
|
|
\subsection{doubleEveryOther}
|
|
This function did not have to use \texttt{foldr}. Here, the idea was to
|
|
use recursion. The recursive case would grab the first two elements and
|
|
the rest of the list. Then, the 2nd element would be doubled, and the
|
|
elements would be appended in order but with the doubleEveryOther
|
|
recursive call for the rest of the list.
|
|
\begin{minted}[frame=single]{haskell}
|
|
doubleEveryOther :: [Int] -> [Int]
|
|
doubleEveryOther [] = []
|
|
doubleEveryOther [x] = [x]
|
|
doubleEveryOther (x:y:xs) = x : myDouble y : doubleEveryOther xs
|
|
\end{minted}
|
|
|
|
\subsection{mySquare}
|
|
Similar to myDouble, but using multiply instead of adding them.
|
|
\begin{minted}[frame=single]{haskell}
|
|
mySquare :: Int -> Int
|
|
mySquare n = foldr (*) 1 [n,n]
|
|
\end{minted}
|
|
|
|
\subsection{sqSum}
|
|
Here, we use a custom lambda function. In addition, because the last
|
|
parameter is the list, we can also use partial function application or
|
|
currying to omit the name of the first argument and make the declaration
|
|
cleaner. The lambda function just adds the square of x to the
|
|
accumulator of \texttt{foldr}.
|
|
\begin{minted}[frame=single]{haskell}
|
|
sqSum :: [Int] -> Int
|
|
sqSum = foldr (\x acc -> mySquare x + acc) 0
|
|
\end{minted}
|
|
|
|
\subsection{sumDigits}
|
|
This function needs to add the digits of all the numbers in the list.
|
|
Here, the custom lambda function uses the previous \texttt{sumList}
|
|
and \texttt{toDigit} functions. It adds the sum of the digits of each
|
|
element to the accumulator.
|
|
\begin{minted}[frame=single]{haskell}
|
|
sumDigits :: [Int] -> Int
|
|
sumDigits = foldr (\x acc -> acc + (sumList $ toDigit x)) 0
|
|
\end{minted}
|
|
|
|
\subsection{sepConcat}
|
|
Concatenate a list of strings while interspersing a separator string in
|
|
between each string. For this, the custom lambda function will prepend
|
|
the string to the accumulator, but only put the separator in between if
|
|
there has already been an element added to the accumulator. This way, we
|
|
avoid the off-by-one error of appending the separator to the end of the
|
|
final string.
|
|
\begin{minted}[frame=single,breaklines=true]{haskell}
|
|
sepConcat :: String -> [String] -> String
|
|
sepConcat sep = foldr (\x acc -> x ++ if acc == [] then acc else sep ++ acc) ""
|
|
\end{minted}
|
|
|
|
\section{Part C: Credit Card Problem}
|
|
This is an application of some of the functions I've written to create a
|
|
function that validates a credit card number using the Luhn algorithm.
|
|
Double every other digit of the card number starting from the end
|
|
(reversed). Take the sum of those digits. If the sum is equal to 0
|
|
mod 10, it is a valid credit card number.
|
|
\begin{minted}[frame=single]{haskell}
|
|
validate :: Int -> Bool
|
|
validate n = myMod (sumDigits (doubleEveryOther (toDigitRev n))) 10 == 0
|
|
\end{minted}
|
|
|
|
\section{Part D: Sorting Algorithms}
|
|
The task for this section was to implement some sorting algorithms in a
|
|
functional manner.
|
|
|
|
\subsection{splitHalf}
|
|
For the \texttt{splitHalf} function, it needs to split a list into a tuple where
|
|
the left and right tuple each have half of the list. To accomplish this,
|
|
I first created a generic \texttt{mySplit} function that splits onto an
|
|
index. This is accomplished by taking in an index and a pair of lists.
|
|
The index is the number of elements from the right pair that should be
|
|
moved into the left pair. So, every recursive call will move the first
|
|
element from the right list into the end of the left list. Then, mySplit
|
|
will be called again with \texttt{n} decremented. The base case, when
|
|
\texttt{n = 0}, is to just return the pair.
|
|
|
|
Finally, \texttt{splitHalf} just calls the \texttt{mySplit} function
|
|
with \texttt{n} equal to half the length of the list.
|
|
\begin{minted}[frame=single]{haskell}
|
|
mySplit 0 p = p
|
|
mySplit n (xs,(y:ys)) = mySplit (n-1) (xs++[y], ys)
|
|
|
|
splitHalf :: [a] -> ([a], [a])
|
|
splitHalf xs = mySplit (div l 2) ([], xs)
|
|
where l = length xs
|
|
\end{minted}
|
|
|
|
\subsection{mergeList}
|
|
This is where it starts to get complicated. \texttt{mergeList} is a
|
|
function that receives two lists of pairs that are sorted by the 2nd
|
|
element of the pair, and merges them into one big sorted list. The way I
|
|
thought about this problem after talking with the professor is to
|
|
consider the first element of each list. Then, I just let the smaller
|
|
one go first and recurse. To accomplish this, I used a bunch of pattern
|
|
matching and a guarded statement. Here, \texttt{axs} and \texttt{ays}
|
|
are the whole lists. Then, \texttt{px} and \texttt{py} are the pairs
|
|
that I should prepend. \texttt{xs} and \texttt{ys} are the rest of the
|
|
list without the first element. Finally, \texttt{x} and \texttt{y} are
|
|
the element the list is sorted by. The guard statement just compares x
|
|
and y, and uses that information to prepend the pair with the smaller
|
|
sorting value.
|
|
\begin{minted}[frame=single]{haskell}
|
|
mergeList :: Ord b => [(a, b)] -> [(a, b)] -> [(a, b)]
|
|
mergeList [] pys = pys
|
|
mergeList pxs [] = pxs
|
|
|
|
mergeList axs@( px@(_,x) : xs) ays@( py@(_,y) : ys)
|
|
| x <= y = px : mergeList xs ays
|
|
| otherwise = py : mergeList axs ys
|
|
\end{minted}
|
|
|
|
\subsection{mergeSort}
|
|
Finally, I got to implement the merge sort algorithm. I first created
|
|
some helper functions. \texttt{applyPair} applies a function $f$ with
|
|
the parameters given by a tuple. \texttt{applyEachPair} applies a
|
|
function $f$ to both elements of a tuple, and returns a new
|
|
tuple. Then, \texttt{mergeSort} puts it all together and applies merge
|
|
sort to each half of the list; then applies mergeList to the pair of the
|
|
sorted halves. The base cases end the recursion of mergeSort, since a
|
|
list of 0 or 1 elements is sorted.
|
|
\begin{minted}[frame=single]{haskell}
|
|
applyPair :: (a->b->c) -> (a,b) -> c
|
|
applyPair f (x,y) = f x y
|
|
|
|
applyEachPair :: (a->b) -> (a,a) -> (b,b)
|
|
applyEachPair f (x, y) = (f x, f y)
|
|
|
|
mergeSort :: Ord b => [(a,b)] -> [(a,b)]
|
|
mergeSort [] = []
|
|
mergeSort [x] = [x]
|
|
mergeSort xs = applyPair mergeList (applyEachPair mergeSort (splitHalf xs))
|
|
\end{minted}
|
|
|
|
\section{Part E: BigInt}
|
|
Here, I was tasked to work with a new type BigInt which is a list of
|
|
integers.
|
|
|
|
\subsection{clone}
|
|
Clone takes a value and a number, and creates a list with that value
|
|
repeated n times. So, a simple recursive solution is to use the cons
|
|
operator and call clone again with n decremented. The base case is when
|
|
n = 0, which returns and empty list.
|
|
\begin{minted}[frame=single]{haskell}
|
|
clone :: a -> Int -> [a]
|
|
clone _ 0 = []
|
|
clone x n = x : clone x (n-1)
|
|
\end{minted}
|
|
|
|
\subsection{padZero}
|
|
This function will take two \texttt{BigInt}s and return a pair of new
|
|
\texttt{BigInt}s that have the same length. Here, I just made a quick
|
|
helper function \texttt{clonez} that clones zero n times. Then, I use a
|
|
guarded expression to prepend zeros based on the difference of the
|
|
lengths. If \texttt{ys} is shorter, it gets \texttt{xl - yl} zeros
|
|
prepended, and vice-versa for \texttt{xs}.
|
|
\begin{minted}[frame=single]{haskell}
|
|
clonez = clone 0
|
|
|
|
padZero :: BigInt -> BigInt -> (BigInt, BigInt)
|
|
padZero xs ys | xl > yl = (xs, clonez (xl-yl) ++ ys)
|
|
| xl < yl = (clonez (yl-xl) ++ xs, ys)
|
|
| otherwise = (xs,ys)
|
|
where xl = length xs
|
|
yl = length ys
|
|
\end{minted}
|
|
|
|
\subsection{removeZero}
|
|
Kind of like the opposite of \texttt{padZero}. Takes a \texttt{BigInt}
|
|
and removes leading zeros that have no value. This is just a simple
|
|
recursive pattern matching. If the first element is zero, remove it and
|
|
call \texttt{removeZero} again. If we can't pattern match a zero, just
|
|
return the list.
|
|
\begin{minted}[frame=single]{haskell}
|
|
removeZero :: BigInt -> BigInt
|
|
removeZero (0:xs) = removeZero xs
|
|
removeZero xs = xs
|
|
\end{minted}
|
|
|
|
\subsection{bigAdd}
|
|
This function will add two \texttt{BigInt}s. To implement this, I
|
|
delegated to a helper function \texttt{bigAdd'}. The reasoning was so I
|
|
could use tail recursion and bring the carry value over each time.
|
|
|
|
First, \texttt{bigAdd'} will take two reversed \texttt{BigInt}s, along
|
|
with a carry bit. We take the sum of the first integers from the lists,
|
|
along with the carry sum. Then, we take the first digit of the sum and
|
|
prepend it, and then recursively call \texttt{bigAdd'} with the carry
|
|
bit being the sum divided by 10, and the rest of the two lists.
|
|
The base case, then, simply takes empty lists and returns the carry bit.
|
|
|
|
The actual implementation of \texttt{bigAdd} pads the incoming
|
|
\texttt{BigInt}s with zeros, reverses them, and calls \texttt{bigAdd'}
|
|
with a carry of 0. Finally, at the end, the result is reversed and
|
|
trimmed of zeros.
|
|
\begin{minted}[frame=single,breaklines=true]{haskell}
|
|
bigAdd' [] [] n = [n]
|
|
bigAdd' (x:xs) (y:ys) n = (myMod sum 10) : bigAdd' xs ys (div sum 10)
|
|
where sum = x+y+n
|
|
|
|
bigAdd :: BigInt -> BigInt -> BigInt
|
|
bigAdd xs ys = removeZero $ reverseList $ bigAdd' (reverseList zxs) (reverseList zys) 0
|
|
where (zxs,zys) = padZero xs ys
|
|
\end{minted}
|
|
|
|
\subsection{mulByDigit}
|
|
I followed a similar strategy to \texttt{bigAdd}. Essentially, the
|
|
helper function \texttt{digMul'} will take a reversed list, and then
|
|
multiply the first digit by some factor q. Then, the carry bit is added,
|
|
and the sum is split into the first digit and the other digits. The
|
|
first digit is prepended to the recursive call of \texttt{digMul'} with
|
|
the rest of the list and the same factor.
|
|
\begin{minted}[frame=single]{haskell}
|
|
digMul' [] _ n = reverseList $ toDigit n
|
|
digMul' (x:xs) q n = (myMod sum 10) : digMul' xs q (div sum 10)
|
|
where sum = (x*q)+n
|
|
|
|
mulByDigit :: Int -> BigInt -> BigInt
|
|
mulByDigit q xs = removeZero $ reverseList $ digMul' (reverseList xs) q 0
|
|
\end{minted}
|
|
|
|
\subsection{bigMul}
|
|
Finally, the \texttt{bigMul} function will take two \texttt{BigInt}s and
|
|
multiply them together. This function follows a basic long
|
|
multiplication algorithm. That is, I take each digit from the second
|
|
number and multiply it digit-by-digit to the first number. Then, based
|
|
on its position, I add zeros to the end of the result. To implement this
|
|
in Haskell, I used a helper function \texttt{megaSum} that recursively
|
|
adds a list of \texttt{BigInt}s using the previously created
|
|
\texttt{bigAdd} function. To create the list of numbers to add, I had to
|
|
reverse the 2nd list \texttt{ys} into \texttt{rys}. Then, by using the
|
|
\texttt{zip} method, I combined each element of \texttt{rys} with its
|
|
index. Since the list was reversed, this index is the number of zeros to
|
|
add onto the sum. Each element of the list passed to \texttt{megaSum} is
|
|
that digit of \texttt{ys}, \texttt{q}, passed to \texttt{mulByDigit}
|
|
with the whole \texttt{BigInt} \texttt{xs}. Finally, zeros are appended
|
|
to the end based on the index. The sum of all of these products is the
|
|
final result.
|
|
\begin{minted}[frame=single,breaklines=true]{haskell}
|
|
megaSum :: [BigInt] -> BigInt
|
|
megaSum [] = [0]
|
|
megaSum (x:xs) = bigAdd x $ megaSum xs
|
|
|
|
bigMul :: BigInt -> BigInt -> BigInt
|
|
bigMul xs ys = megaSum [mulByDigit q xs ++ clone 0 i | (i,q) <- zip [0..] rys]
|
|
where rys = reverseList ys
|
|
\end{minted}
|
|
|
|
\section{Results}
|
|
The Autograder on EDORAS gave me full marks.
|
|
|
|
\begin{Verbatim}[label={Results},frame=single]
|
|
---------------------------------------------------------------------------
|
|
Results: [90.00/90.00] | [100.00%]
|
|
---------------------------------------------------------------------------
|
|
\end{Verbatim}
|
|
|
|
% \inputminted[label={SolutionPA2.hs},breaklines=true]{haskell}{SolutionPA2.hs}
|
|
|
|
\end{document}
|