dyslexic
edit
qrcode
+=-

Assignment 12 - Paradigm Functional: Haskell


Individual, Due: 2025 Aug 26, 11:59pm

IMPORTANT NOTE


Important

There are solutions to these problems on the web. Do not look at any of these solutions, not even for a hint! It is your responsibility to maintain academically honest. The solution is not difficult, but it is in your interest to fully understand how this is working.

Overview


The purpose of this assignment is to write Haskell functions to generate Moessner Miracle numbers. We will take advantage of the lazy evaluation feature of Haskell to generate and work with infinite lists.

This assignment is based on the video The Moessner Miracle. Why Wasn't this discovered for over 2000 years? by Mathologer.

The Moessner Miracle method can generate number sequences of various running sums, powers, or factorials, all without using multiplication. For example, we can use this method to generate all the positive squares (i.e., \(1^2=1\), \(2^2=4\), \(3^2=9\), \(4^2=16\), \(5^2=25\), ...).

We will use a combination of simple functions to generate number sequences. The first sequence of numbers are all the positive squares (\(1^2=1\), \(2^2=4\), \(3^2=9\), \(4^2=16\), ...), positive cubes (\(1^3=1\), \(2^3=8\), \(3^3=27\), \(4^3=64\), ...), and so on. The second sequence of numbers are factorials of positive integers (\(1!=1\), \(2!=2\), \(3!=6\), \(4!=24\), ...).

The simple functions used will operate on infinite lists to perform these operations:

Be sure to watch The Moessner Miracle. Why Wasn't this discovered for over 2000 years? by Mathologer.

IMPORTANT NOTE


Important

There are solutions to these problems on the web. Do not look at any of these solutions, not even for a hint! It is your responsibility to maintain academically honest.

Yes, I've said this already. It is important enough to mention it at least twice.

Requirements


Write the following Haskell functions. Full descriptions follow below along with examples at the bottom of the Requirements section.

skip_every_n :: Int -> [Int] -> [Int] filter out every \(n\)th element of a list
skip_growing :: [Int] -> [Int] filter out elements between growing runs
running_sum :: [Int] -> [Int] compute running sums
moessner_powers :: Int -> [Int] compute powers sequence
moessner_factorials :: [Int] compute factorial sequence

For each of the functions above, you must demonstrate that your implementation is working correctly. To do this, include sample code and screenshots in your readme.html.

skip_every_n :: Int -> [Int] -> [Int] will filter out every \(n\)th element (first argument) in the given list. Note: the first element is included in the output! Only elements at indices that are multiples of the given Int are filtered out (starting indices at 1). See Mathologer video starting at 2:27.

skip_growing :: [Int] -> [Int] will filter out elements like skip_every_n, but with growing number of included elements. Here is a simple way to understand this function: include zero items, then skip one, then include one item, then skip one, include two items, skip one, include three items, skip one, include four items, skip one, and so on. Note: the first element is not included in the output! See Mathologer video starting at 9:59.

running_sum :: [Int] -> [Int] will compute the running sum of the given list. The first element is included; then the first and second elements are summed together; then the first, second, and third elements are summed; then the first through fourth elements are summed; and so on. IMPORTANT: for the \(n\)th output, you do not need to recompute the sum of first element to the \((n-1)\)th element, because that result was the \((n-1)\)th output you just previously computed! See Mathologer video starting at 2:38.

moessner_powers :: Int -> [Int] will produce a sequence of powers, \(i^n\) for a given \(n\), such as squares or cubes. See Mathologer video starting at 2:24.

moessner_factorials :: [Int] will produce a sequence of factorials, \(i!\). See Mathologer video starting at 9:54.

To get full points for...

Example Outputs

Below is a table showing some example outputs for the functions you must implement. The first row is used as example input for skip_every_n, skip_growing, and running_sum. Note: your implementations should work with infinite lists to receive full credit!

list = [1..10] : 1 2 3 4 5 6 7 8 9 10
skip_every_n 2 list : 1 (skip) 3 (skip) 5 (skip) 7 (skip) 9 (skip)
skip_every_n 3 list : 1 2 (skip) 4 5 (skip) 7 8 (skip) 10
skip_growing list : (skip) 2 (skip) 4 5 (skip) 7 8 9 (skip)
running_sum list : 1 3 6 10 15 21 28 36 45 55
moessner_powers 2 : 1 4 9 16 25 36 49 64 81 100 ...
moessner_powers 3 : 1 8 27 64 125 216 343 512 729 1,000 ...
moessner_factorials : 1 2 6 24 120 720 5,040 40,320 362,880 3,628,800 ...

The (skip) table entries are there to give a visual indication of the values from list argument that are skipped over in the output.

Integer Overflow

The Int type in haskell is a fixed-width machine-specific integer with a minimum guaranteed range of \(-2^{29}\) to \(-2^{29}-1\). This is sufficient to computer up to \(20!\), but \(21!\) will cause an overflow, producing a negative result. Switching from Int types to Integer will prevent this, as the Integer type has arbitrary-precision.

Note

Integer types use more memory and are slower to use than Int.

IMPORTANT NOTE


Important

Just to make sure that you truly understand, here is the important note, again. Do not look at any other solution, not even for a hint! It is your responsibility to maintain academically honest.

Extra Credit


Note

You will not receive any points for extra credit implementations if your solution does not receive at least 2 points for each of the required implementations (skip_every_n, skip_growing, running_sum, moessner_powers, moessner_factorials). In other words, you cannot make up for not doing required implementations by doing the extra credit implementations instead.

If you choose to implement these extra credit problems, be prepared to answer any question I may have about your implementation. I may call upon you to justify, clarify, or explain to me what you did. Again, these solutions must be developed, written, debugged by you. Do not use any other resource.

Also, you are responsible for testing your implementations and proving to me that you have tested your implementations. Be certain to test you implementations sufficiently against edge cases! Show your tests in your readme.html.

Implement the following functions (full descriptions below).

mergesort :: [Int] -> [Int] sorts list using merge sort algorithm
quicksort :: [Int] -> [Int] sorts list using quicksort algorithm
infix2rpn :: String -> String converts expression from infix to reverse polish notation
evalrpn :: String -> Int evaluates expression given in reverse polish notation

Write a mergesort :: [Int] -> [Int] function that sorts the input list of integers using merge sort technique. Much like the Sieve problem above, you will use multiple functions to solve this problem. Note: Merge sort runs in \(O(n \log n)\) time, therefore your implementation should also run in \(O(n \log n)\) to receive credit.

Write a quicksort :: [Int] -> [Int] function that sorts the input list of integers using quicksort technique. Note: Quicksort run in \(O(n \log n)\) time (assuming a good pivot is chosen), therefore your implementations should also run in the same time to receive credit.

Write an infix2rpn :: String -> String function that converts an expression (first parameter) from infix to reverse polish notation. For example, infix2rpn "3 + 4 * 5" will result in "3 4 5 * +". You can assume a valid infix expression consisting of only numbers and the + or * operators with spaces between. Tip: use the words :: String -> [String] function to convert the input parameter to a list of strings by splitting on spaces, and use the unwords :: [String] -> String function to convert a list of strings into a single string (with spaces between the "words").

Extend the infix2rpn function to accept parentheses for additional extra credit. For example, infix2rpn "( 3 + 4 ) * 5" returns 3 4 + 5 *.

Write an evalrpn :: String -> Int function that evaluates an expression in reverse polish notation. For example, evalrpn "3 4 5 * +" evaluates to 23. After using the words function on the input, you will need to use the read :: Read a => String -> a function to convert a String to an Int. For example, read "42" :: Int returns 42, while read "42" :: Float returns 42.0. The :: Int or :: Float determine which type (a in function definition) to convert the string into. Another example, let c a = (read a :: Int) in map c ["3","41","5"] returns [3,41,5].

If you have implemented the last three extra credit problems, calling evalrpn (infix2rpn "( 3 + 4 ) * 5") should return 35.

Feel free to add the following convenience function to your Haskell file. I wrote this function to be understandable, not necessarily efficient.

-- Similar to words function, but handles missing/extra spaces
-- between ops, digits, parens.  Note: not general or robust!
mywords :: String -> [String]
mywords s = parser [] s
    where
        isparen c = (c == '(' || c == ')')
        isop    c = (c == '+' || c == '*')
        isdigit c = (c >= '0' && c <= '9')
        -- readint reads digits until non-digit
        -- returns ("digit","remainder of string")
        readint "" = ("","")
        readint (h:t)
            | (isdigit h) = (h:(fst (readint t)), snd (readint t))
            | otherwise  = ("", h:t)
        parser l "" = l
        parser l (h:t)
            | (isdigit h) = parser (l ++ [fst (readint (h:t))])
                                   (snd (readint (h:t)))
            | (isop h)    = parser (l ++ [h:""]) t
            | (isparen h) = parser (l ++ [h:""]) t
            | h == ' '    = parser l t

Using mywords instead of words allows you to specify the infix expression without needing to separate out each entity (digit, op, parenthesis) with spaces. For example:

evalrpn (infix2rpn " (3+  4) *5 ")

IMPORTANT NOTE


Important

Seriously, don't look at others' solutions! It is your responsibility to maintain academically honest.

Resources


Several free versions of Haskell are available. See the Notes.

Grade Template


Implementation
3pts Correct skip_every_n function
3pts Correct skip_growing function
3pts Correct running_sum function
3pts Correct moessner_powers function
3pts Correct moessner_factorials function
Submission
3pts Submitted Haskell source (.hs)
3pts Include testing code in readme.html as codeblock
3pts readme.html with screenshots
Extra Credit
+3pts Correct mergesort implementation
+3pts Correct quicksort implementation
+3pts Correct infix2rpn implementation
+3pts Extend infix2rpn to handle parentheses
+3pts Correct evalrpn implementation

Getting Help


This is an individual assignment. If you have questions about your general solution or about your particular implementation (e.g., your code), ask the prof or TA!

need help with ask
understanding the problem professor, TA, or peer
developing a solution professor or TA
implementing and debugging professor or TA

Do NOT use any other resource for help, hints, etc. Your submission should be your own work, and your work alone!

Submit


Submit via course LMS a Zip file containing the Haskell source, readme.html, and evidence of your code working (screen snapshot).

If you did the extra credit, include screenshots in the readme.html.