$29
This assignment provides experience in Haskell programming. Please compile and run your code on command line using Haskell GHC compiler. You may download GHC at https://www.haskell.org/platform/.
Turning in your assignment
The problem solution will consist of a sequence of function definitions and unit tests for those functions. You will write all your functions in the attached HW1.hs file. You can edit this file and write code using any source code editor (Notepad++, Sublime, Visual Studio Code, etc.). We recommend you to use Visual Studio Code, since it has better support for Haskell.
In addition, you will write unit tests using HUnit testing package. Attached file, HW1SampleTests.hs, includes 3 to 4 test cases tests for each problem. You will edit this file and
provide additional tests for each problem (at least 2 tests per problem). Please rename your test file as HW1Tests.hs.
The instructor will show how to import and run tests on GHCI during the lecture. Please refer to the lecture video (January 29) on Blackboard.
To submit your assignment, please upload both files (HW1.hs and HW1Tests.hs) on the Assignment1 (Haskell) DROPBOX on Blackboard (under Assignments). You may turn in your assignment up to 3 times. Only the last one submitted will be graded.
The work you turn in is to be your own personal work. You may not copy another student's code or work together on writing code. You may not copy code from the web, or anything else that lets you avoid solving the problems for yourself. At the top of the file in a comment, please include your name and the names of the students with whom you discussed any of the problems in this homework. This is an individual assignment and the final writing in the submitted file should be *solely yours*.
Important rules
• Unless directed otherwise, you must implement your functions using recursive definitions built up from the basic built-in functions. (You are not allowed to import an external library and use functions from there.)
• The type of your functions should match with the type specified in each problem. You don’t need to include the “type signatures” for your functions.
• Make sure that your function names match the function names specified in the assignment specification. Also, make sure that your functions work with the given tests. However, the given test inputs don’t cover all boundary cases. You should generate other test cases covering the
extremes of the input domain, e.g. maximum, minimum, just inside/outside boundaries, typical values, and error values.
• When auxiliary functions are needed, make them local functions (inside a let..in or where
block). In this homework you will lose points if you don’t define the helper functions inside a let..in or where block.
• Be careful about the indentation. The major rule is “code which is part of some statement should be indented further in than the beginning of that expression”. Also, “if a block has multiple statements, all those statements should have the same indentation”. Refer to the following link for more information: https://en.wikibooks.org/wiki/Haskell/Indentation
• The assignment will be marked for good programming style (indentation and appropriate comments), as well as clean compilation and correct execution. Haskell comments are placed inside properly nested sets of opening/closing comment delimiters:
{- multi line comment-}.
Line comments are preceded by double dash, e.g., -- line comment
Problems
1. exists and countInList – 15%
a) [5pts] Write a function exists which takes a “value” and a “list” as input. If the value is a member of the list, the function should return True. Otherwise it should return False.
The function should have type exists :: Eq t => t -> [t] -> Bool. (You are not allowed to use the elem function in your exists implementation.)
Examples:
• exists 1 [] False
• exists 1 [1,2,3] True
• exists [1] [[1]] True
• exists [1] [[3],[5]] False
• exists '3' "CptS355" True
b) [2pts] Explain in a comment why the type is exists :: Eq t => t -> [t] -> Bool
but not
exists :: t -> [t] -> Bool
c) [8pts] Write a function countInList which takes a value and a list and returns the number of occurrences of that value in the input list.
The type of your function should be countInList :: (Num p, Eq t) => t -> [t] -> p.
Examples:
• countInList "5" ["3","5","5","-","4","5","1"]
3
• countInList "5" []
0
• countInList True [True, False, False, False, True, True, True]
4
• countInList [] [[],[1,2],[3,2],[5,6,7],[8],[]]
2
2. listDiff - 15%
Write a function listDiff that takes two lists as input and returns the difference of the first list with respect to the second. The input lists may have duplicate elements. If an element appears in both lists and if the number of duplicate copies of the element is bigger in the first list, then this element should appear in the result as many times as the difference of the number of occurrences in the input lists.
Your function should have type listDiff :: Eq a => [a] -> [a] -> [a]. The duplicates should not be eliminated in the result. The elements in the resulting list may have arbitrary order. (Hint: You can make use of countInList function in your solution.)
Examples:
• listDiff ["a","b","c"] ["b"] ["a","c"]
• listDiff [1,2,3] [1,1,2]
[3]
• listDiff [1,2,2,3,3,3] [1,1,2,3] [2,3,3]
• listDiff [[2,3],[1,2],[2,3]] [[1],[2,3]] [[2,3],[1,2]]
• listDiff [1,2,3] []
[1,2,3]
3. firstN – 15%
Write a function firstN that takes a list and a number n and returns the first n elements in the list.
The type of firstN can be either of the following:
firstN :: (Num t) => [a] -> t -> [a]
firstN :: (Eq t, Num t) => [a] -> t -> [a]
firstN :: (Ord t, Num t) => [a] -> t -> [a]
If the input list is empty or if the length of the list is less than n, then the function should return the complete list. (You may assume that n is greater than 0.)
Examples:
• firstN ["a", "b", "c", "x", "y"] 3 ["a", "b", "c"]
• firstN [1,2,3,4,5,6,7] 4
[1,2,3,4]
• firstN [1,2,3,4,5,6,7] 10 [1,2,3,4,5,6,7]
• firstN [[1,2,3],[4,5],[6],[],[7,8],[]] 4 [[1,2,3],[4,5],[6],[]]
• firstN [] 5
[]
4. busFinder – 20%
Pullman Transit offers many bus routes in Pullman. Assume that they maintain the bus stops for their routes as a list of tuples. The first element of each tuple is the bus route and the second element is the list of stops for that route (see below for an example).
buses = [("Lentil",["Chinook", "Orchard", "Valley", "Emerald","Providence", "Stadium", "Main", "Arbor", "Sunnyside", "Fountain", "Crestview", "Wheatland", "Walmart", "Bishop", "Derby", "Dilke"]),
("Wheat",["Chinook", "Orchard", "Valley", "Maple","Aspen", "TerreView", "Clay", "Dismores", "Martin", "Bishop", "Walmart", "PorchLight", "Campus"]),
("Silver",["TransferStation", "PorchLight", "Stadium", "Bishop","Walmart", "Shopco", "RockeyWay"]),
("Blue",["TransferStation", "State", "Larry", "TerreView","Grand", "TacoBell", "Chinook", "Library"]),
("Gray",["TransferStation", "Wawawai", "Main", "Sunnyside","Crestview", "CityHall", "Stadium", "Colorado"])
]
Assume that you are creating an application for Pullman Transit. You would like to write an Haskell function busFinder that takes the list of bus routes and a stop name, and returns the list of the bus routes which stop at the given bus stop.
The type of busFinder should be busFinder :: Eq t => t -> [(a, [t])] -> [a].
(Hint: You can make use of exists function you defined earlier.)
Examples:
• busFinder "Walmart" buses ["Lentil","Wheat","Silver"]
• busFinder "Shopco" buses ["Silver"]
• busFinder "Main" buses ["Lentil","Gray"]
• busFinder "Beasley" buses
[]
5. cumulativeSums – 15%
Write a function cumulativeSums that takes a list of numbers and returns a list including the partial sums of these numbers.
The type of cumulativeSums should be: cumulativeSums :: Num a => [a] -> [a]
(Hint: Define and use a helper function that takes a list and a number holding the accumulated sum. )
Examples:
• cumulativeSums [1,2,3,4,5,6,7,8,9,10] [1,3,6,10,15,21,28,36,45,55]
• cumulativeSums [5,5,5,5,5,5,5] [5,10,15,20,25,30,35]
• cumulativeSums [1,2,3,4,-4,-3,-2] [1,3,6,10,6,3,1]
• cumulativeSums []
[]
6. groupNleft - 20%
groupNleft function takes two arguments where the first argument is a number (n) and the second is a list (iL). The goal is to produce a result in which the elements of the original list have been collected into ordered sub-lists each containing n elements (where n is the number argument). The leftover elements (if there is any) are included as a sublist with less than n elements. If iL is empty, then function should return [] (will also accept [[]]).
The type of groupNleft can be one of the following: groupNleft :: Int -> [a] -> [[a]] groupNleft :: (Num t) => t -> [a] -> [[a]]
Note: this function is not required to be tail-recursive.
Examples:
• groupNleft 3 [1, 2, 3, 4, 5, 6, 7, 8] [[1,2,3],[4,5,6],[7,8]]
• groupNleft 5 [1, 2, 3, 4, 5, 6, 7, 8] [[1,2,3,4,5],[6,7,8]]
• groupNleft 2 [(1,"a"),(2,"b"),(3,"c"),(4,"d"),(5,"e"),(6,"f")] [[(1,"a"),(2,"b")],[(3,"c"),(4,"d")],[(5,"e"),(6,"f")]]
• groupNleft 2 [1, 2, 3, 4, 5, 6, 7, 8] [[1,2],[3,4],[5,6],[7,8]]
• groupNleft 3 []
[] -- will also accept [[]]
Testing your functions
We will be using the HUnit unit testing package in CptS355. See
http://hackage.haskell.org/package/HUnit for additional documentation.
You may install HUnit in 3 different ways:
Option1 (using cabal installer) :
Run the following commands on the terminal.
cabal update
cabal v2-install --lib HUnit
Option2 (from source) :
1. First install call-stack package
◦ Download the package from here: http://hackage.haskell.org/package/call-stack . Download the call-stack-0.2.0.tar.gz file and extract it.
(To extract .gz and .tar files on Windows you may use 7zip (https://www.7-zip.org/))
• Then switch to the call-stack directory and install call-stack using the following commands at the terminal/command prompt:
runhaskell Setup configure
runhaskell Setup build
runhaskell Setup install
▪ If you get "permission denied" errors:
• on Windows, run the terminal or command line as administrator.
• on Mac and Linux, run the command in 'sudo' mode (or login as root).
2. Next install HUnit package:
◦ Download the package from here: http://hackage.haskell.org/package/HUnit. Download the HUnit-1.2.5.2.tar.gz file and extract it.
◦ Then switch to the HUnit directory and install HUnit using the following commands at the terminal/command prompt:
runhaskell Setup configure
runhaskell Setup build
runhaskell Setup install
If you get "permission denied" errors, follow the above guideline.
Option3 (using stack – use this option if you already installed stack ) :
Run the following command on the terminal.
stack install Hunit
Running Tests
The file HW1SampleTests.hs provides 3 sample test cases comparing the actual output with the expected (correct) output for each problem. This file imports the HW1 module (HW1.hs file) which will include your implementations of the given problems.
You are expected to add at least 2 more test cases for each problem. Make sure that your test inputs cover all boundary cases.
In HUnit, you can define a new test case using the TestCase function and the list TestList includes the list of all test that will be run in the test suite. So, make sure to add your new test cases to the TestList list. All tests in TestList will be run through the "runTestTT tests" command. The instructor will further explain this during the lecture.
If you don’t add new test cases you will be deduced at least 5% in this homework.
Haskell resources:
• Learning Haskell, by Gabriele Keller and Manuel M T Chakravarty (http://learn.hfm.io/)
• Real World Haskell, by Bryan O'Sullivan, Don Stewart, and John Goerzen (http://book.realworldhaskell.org/)
• Haskell Wiki: https://wiki.haskell.org/Haskell
• HUnit: http://hackage.haskell.org/package/HUnit