This commit is contained in:
Citlali del Rey 2024-03-19 10:58:08 -07:00
parent 0151948fb9
commit 923dd2e2a5
Signed by: nullobsi
GPG Key ID: 933A1F44222C2634
5 changed files with 801 additions and 32 deletions

303
.gitignore vendored
View File

@ -7,3 +7,306 @@ a.out
.DS_Store
**/.DS_Store
# TeX
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
*.lzo
*.lzs
*.slg
*.slo
*.sls
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplot
*.gnuplot
*.table
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.glog
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
# *.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# newpax
*.newpax
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# svg
svg-inkscape/
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# titletoc
*.ptc
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices and outlines
*.xyc
*.xyd
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# gummi
.*.swp
# KBibTeX
*~[0-9]*
# TeXnicCenter
*.tps
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
# Makeindex log files
*.lpz
# xwatermark package
*.xwm
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
# Uncomment the next line to have this generated file ignored.
#*Notes.bib

View File

@ -136,11 +136,12 @@ myDouble n = foldr (+) 0 [n,n]
-- >>> [1,4,3]
doubleEveryOther :: [Int] -> [Int]
-- I don't like this code. It doesn't feel very Haskell.
-- Use list comprehension to get all the indices of xs.
-- Then if the index is odd, double the value.
-- This will double every other starting with the 2nd digit.
doubleEveryOther xs = [if myMod i 2 == 0 then xs !! i else myDouble (xs!!i) | i <- [0..(length xs) - 1]]
-- Base cases.
doubleEveryOther [] = []
doubleEveryOther [x] = [x]
-- In the recursive case, just multiply the 2nd element from the start.
-- Then doubleEveryOther on the rest of the list.
doubleEveryOther (x:y:xs) = x : myDouble y : doubleEveryOther xs
--mySquare
--Write your own my square function using foldr
@ -171,9 +172,8 @@ mySquare n = foldr (*) 1 [n,n]
-- 30
sqSum :: [Int] -> Int
-- We can use foldr and list comprehension to add evertything in a new
-- list where mySquare was applied (map).
sqSum ns = foldr (+) 0 [mySquare x | x <- ns]
-- The lambda function should square and then add to accumulator.
sqSum = foldr (\x acc -> mySquare x + acc) 0
--sumDigits is to add the sum of all the number inside the list that is already turn into single digit (10 pts)
@ -185,11 +185,9 @@ sqSum ns = foldr (+) 0 [mySquare x | x <- ns]
-- >>> 10
sumDigits :: [Int] -> Int
-- We can use a double list comprehension combined with foldr.
-- Take every n from ns.
-- Find the digits, and then get every digit x.
-- Combine it all into a list and apply foldr.
sumDigits ns = foldr (+) 0 [x | n <- ns, x <- toDigit n]
-- The lambda will take the sum of the list of digits of each element
-- and add it to the accumulator.
sumDigits = foldr (\x acc -> acc + (sumList $ toDigit x)) 0
-- sepConcat will concatenate the defined seperator to a list of string. If the list is empty despite the defined seperator return empty string.
--
@ -203,14 +201,10 @@ sumDigits ns = foldr (+) 0 [x | n <- ns, x <- toDigit n]
-- >>> "a#b#c#d#e"
sepConcat :: String -> [String] -> String
-- Base case 1, when the list is empty.
sepConcat _ [] = []
-- Pattern match a list with only one element.
-- When we reach this case, we don't add separator to the end.
sepConcat _ (t:[]) = t
-- On the recursive case, we patten match the first element and the rest
-- of the list. We then concat it with the separator and recurse.
sepConcat sep (t:ts) = t ++ sep ++ sepConcat sep ts
-- The lambda in foldR will prepend the strings+separator to the accumulator but
-- skip the separator in the beginning so that the string doesn't end in
-- a separator.
sepConcat sep = foldr (\x acc -> x ++ if acc == [] then acc else sep ++ acc) ""
-- Part C: Credit Card problem
@ -290,7 +284,7 @@ mergeList axs@( px@(_,x) : xs) ays@( py@(_,y) : ys)
-- If x <= y, the first pair ps should go first.
-- Then we can merge the rest of xs with all of ays.
| x <= y = px : mergeList xs ays
-- Otherwise, we can just call mergeList again.
-- Otherwise, we can just call mergeList again and prepend py.
| otherwise = py : mergeList axs ys
@ -341,8 +335,6 @@ type BigInt = [Int]
clone :: a -> Int -> [a]
-- Base case
clone _ 0 = []
-- List with just x
clone x 1 = x : []
-- Create a list with [x], then recursive call function.
clone x n = x : clone x (n-1)
@ -383,8 +375,6 @@ padZero xs ys | xl > yl = (xs, clonez (xl-yl) ++ ys)
-- >>> []
removeZero :: BigInt -> BigInt
-- Base case
removeZero [] = []
-- If we can pattern match a zero, recurse with rest of list.
removeZero (0:xs) = removeZero xs
-- Otherwise just return it.
@ -399,8 +389,22 @@ removeZero xs = xs
-- bigAdd [9, 9, 9, 9] [9, 9, 9]
-- >>> [1, 0, 9, 9, 8]
--bigAdd :: BigInt -> BigInt -> BigInt
bigAdd _ _ = "not implemented"
-- Takes reverse.
-- Base case, we use n as the carry bit, and so we just leave it at the
-- end.
bigAdd' [] [] n = [n]
-- For recursion, we use mod with the sum to get the digit,
-- and then we recurse with the carry bit being the integer division of
-- 10.
bigAdd' (x:xs) (y:ys) n = (myMod sum 10) : bigAdd' xs ys (div sum 10)
where sum = x+y+n
bigAdd :: BigInt -> BigInt -> BigInt
-- We use bigAdd' after we zero-pad the incoming bigInts.
-- Since bigAdd' wants reversed, we reverse the lists. We finally have
-- to reverse the list at the end again.
bigAdd xs ys = removeZero $ reverseList $ bigAdd' (reverseList zxs) (reverseList zys) 0
where (zxs,zys) = padZero xs ys
-- `mulByDigit i n` returns the result of multiplying
@ -409,8 +413,20 @@ bigAdd _ _ = "not implemented"
-- mulByDigit 9 [9,9,9,9]
-- >>> [8,9,9,9,1]
--mulByDigit :: Int -> BigInt -> BigInt
mulByDigit _ _ = "not implemented"
-- Takes reverse.
-- Base case will just return the carry digits.
digMul' [] _ n = reverseList $ toDigit n
-- For recursion, we use mod with the sum to get the digit,
-- and then we recurse with the carry-over being the integer division of
-- 10, where the sum is the digit in the list * q, then added the
-- carry-over.
digMul' (x:xs) q n = (myMod sum 10) : digMul' xs q (div sum 10)
where sum = (x*q)+n
-- Just delegate to digMul' after reversing the list.
mulByDigit :: Int -> BigInt -> BigInt
mulByDigit q xs = removeZero $ reverseList $ digMul' (reverseList xs) q 0
-- `bigMul n1 n2` returns the `BigInt` representing the
@ -422,7 +438,27 @@ mulByDigit _ _ = "not implemented"
-- bigMul [9,9,9,9,9] [9,9,9,9,9]
-- >>> [9,9,9,9,8,0,0,0,0,1]
--bigMul :: BigInt -> BigInt -> BigInt
bigMul _ _ = "not implemented"
-- Long multiply algorithm:
-- For each digit in ys, multiply xs by it.
-- Based on the index, shift the whole thing by the number of powers of
-- 10.
-- Then, just bigSum them all.
-- Helper function.
-- Like sumList but for bigInts.
-- So megaSum.
megaSum :: [BigInt] -> BigInt
megaSum [] = [0]
megaSum (x:xs) = bigAdd x $ megaSum xs
bigMul :: BigInt -> BigInt -> BigInt
-- Implementation: use megaSum on a list of bigInts.
-- We use zip with [0..] and ys to get tuples of the index and the
-- value.
-- Then, we can use mulByDigit to multiply xs by the digit of y, then
-- use the index to add the correct number of zeros to the end.
-- Finally the megaSum adds all of the resulting bigInts.
bigMul xs ys = megaSum [mulByDigit q xs ++ clone 0 i | (i,q) <- zip [0..] rys]
where rys = reverseList ys

BIN
Assignment2/report2.pdf Normal file

Binary file not shown.

421
Assignment2/report2.tex Normal file
View File

@ -0,0 +1,421 @@
%! 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}

9
Week8/lecture1.hs Normal file
View File

@ -0,0 +1,9 @@
fact :: [Int]
fact = scanl (*) 1 [2..]
fibs :: [Int]
-- to understand recursion one must first understand recursion...
-- F_n+2 = 1 + sum(F_1 .. F_n)
fibs = 1 : scanl (+) 1 fibs