-- Originally done here: https://github.com/Syndamia/IT-kariera/blob/master/Year%203/Functional%20programming/Exam%20preparation/LINQuistics.hs -- At the bottom you can find the problem this solves (Note: it was originally made for C# with Linq) {- Split -} splitAtSection :: String -> Int -> [Int] splitAtSection str start = if start == length str then [start, 0] else if str!!start == '.' then [start, 1] else if str!!start == '(' then [start, 3] else splitAtSection str (start + 1) splitInput :: String -> [String] splitInput line = if null line then [] else [take (head (splitAtSection line 0)) line] ++ splitInput (drop (sum (splitAtSection line 0)) line) {- Insert -} sanitizeArr :: Eq a => [a] -> [a] sanitizeArr arr = if null arr then [] else if elem (last arr) (init arr) then sanitizeArr (init arr) else (sanitizeArr (init arr)) ++ [last arr] insertIncl :: [[String]] -> [String] -> [[String]] insertIncl mat info = if indexOfHead mat (head info) 0 < 0 then mat ++ [[head info] ++ sanitizeArr (tail info)] else insertInfo mat (indexOfHead mat (head info) 0) (tail info) where insertInfo mat index info = take index mat ++ [[head (mat!!index)] ++ sanitizeArr ((tail (mat!!index)) ++ info)] ++ drop (index + 1) mat {- Sort -} desc :: (Ord a) => [a] -> (a -> a -> Bool) -> (a -> a -> Bool) -> Int -> Int -> [a] desc arr cond scond end curr = if end == 0 then arr else if curr == end then desc arr cond scond (end - 1) 0 else if cond (arr!!curr) (arr!!(curr + 1)) || scond (arr!!curr) (arr!!(curr+1)) then desc (swapTwo arr curr) cond scond end (curr + 1) else desc arr cond scond end (curr + 1) where swapTwo arr i = take i arr ++ [arr!!(i + 1)] ++ [arr!!i] ++ drop (i + 2) arr orderByDesc :: (Ord a) => [a] -> (a -> a -> Bool) -> (a -> a -> Bool) -> [a] orderByDesc arr cond scond = desc arr cond scond (length arr - 1) 0 {- Sort conditions -} compByUniqCnt :: String -> String -> Bool compByUniqCnt x y = length x == length y && uniqCnt x < uniqCnt y where uniqCnt str = length (foldl (\acc x -> if elem x acc then acc else acc ++ [x]) [] str) compByLen :: (Ord a) => [a] -> [a] -> Bool compByLen x y = length x < length y compByShort :: (Ord a) => [a] -> [a] -> Bool compByShort x y = length x > length y compByShortestName :: [String] -> [String] -> Bool compByShortestName x y = length (last (orderByDesc (tail x) compByLen alwaysFalse)) < length (last (orderByDesc (tail y) compByLen alwaysFalse)) alwaysFalse :: a -> a -> Bool alwaysFalse x y = False {- Filters -} elemsThatContain :: [[String]] -> String -> [[String]] elemsThatContain mat item = if null mat then [] else if elem item (head mat) then [head mat] ++ elemsThatContain (tail mat) item else elemsThatContain (tail mat) item getCollWithMostMethods :: [[String]] -> [String] getCollWithMostMethods mat = mat!!(indexOfMostMethods lengthsArr) where lengthsArr = map (\x -> length x) mat indexOfMostMethods arr = foldl (\acc x -> if arr!!x > arr!!acc then x else acc) 0 [1..(length mat - 1)] {- Printing -} printMethods arr = if null arr then return () else do putStrLn ("* " ++ (head arr)) printMethods (tail arr) printCollection :: [[String]] -> Bool -> IO () printCollection collection haveMethods = do if null collection then return () else do putStrLn (head (head collection)) if haveMethods then printMethods (orderByDesc (tail (head collection)) compByLen alwaysFalse) else return () printCollection (tail collection) haveMethods {- Main -} indexOfHead :: [[String]] -> String -> Int -> Int indexOfHead mat key start = if null mat then (-1) else if head (head mat) == key then start else indexOfHead (tail mat) key (start + 1) isNumber :: String -> Bool isNumber str = if null str then False else (head str >= '0' && head str <= '9') && ((length str > 1) == isNumber (tail str)) readUntilWord :: [[String]] -> IO () readUntilWord mat = do line <- getLine let coll = splitInput line if head coll == "exit" then do final <- getLine let comm = words final let collecs = elemsThatContain mat (head comm) let ordered = orderByDesc collecs compByLen compByShortestName printCollection ordered (comm!!1 == "all") else if length coll == 1 then do if isNumber (head coll) then do let mostMethodsOrd = orderByDesc (tail (getCollWithMostMethods mat)) compByShort alwaysFalse let n = read (head coll) :: Int printMethods (take n mostMethodsOrd) else if indexOfHead mat (head coll) 0 >= 0 then do let methods = orderByDesc (tail (mat!!(indexOfHead mat (head coll) 0))) compByLen compByUniqCnt printMethods methods else return() readUntilWord mat else readUntilWord (insertIncl mat coll) main :: IO() main = do readUntilWord [] -- -- LINQuistics -- -- LINQ is the greatest .NET component of all time. You can do almost anything with it. That’s why you have been tasked to do almost everything you can with it. -- -- --------- -- - Input - -- --------- -- -- You will be given several input lines containing information about collections, and LINQ methods that have been called on them, in the following format: -- -- {collection}.{method1}().{method2}()....{methodN}() -- -- The count of methods may vary. Your task is to store every collection and the methods that have been executed on it. -- If the collection already exists, you must ADD the new methods to it. Duplicate methods should be REMOVED. -- -- ---------- -- - Output - -- ---------- -- -- If you are given only a collection name, you must print the methods that have been executed on the collection, ordered by their length in descending order. -- If 2 methods have the same length, order them by the count of unique symbols they have in their names in descending order. -- -- Each method must be printed on a new line, with a prefixed asterisk and space (“* ”). -- -- If the collection name does NOT exist, you should IGNORE that line of input. -- -- If you are given only a digit, you must take the collection which has the most methods, and print the first N methods, with the lowest length (N being the digit given in the input). -- If there are less than N methods you must print all of them in the same order. -- -- NOTE: When printing, you must print only the method name, without its brackets (e.g. “First”, not “First()”). -- -- The input sequence ends when you receive the command “exit”. After the ending command, you will receive one last line in the following format: -- -- {method} {selection} -- -- You must take all collections, which CONTAIN the given method, and print them. The selection will either be “collection” or “all”. -- If you have “collection”, you must print only the collections’ names in the final output. -- If you have “all”, you must print the collections and their methods in the following format: -- -- {collection} -- * {method1} -- * {method2} -- ... -- -- The collections must be printed ordered by the count of their methods in descending order. -- -- If 2 collections have the same amount of methods, print the one whose shortest method name is longer than the other one’s shortest method name. -- -- The methods must be printed, ordered by their length in descending order. -- -- ------------ -- - Examples - -- ------------ -- -- -- Input ------------------------------- -- participants.Max().Reverse().ThenBy() -- participants.OrderBy.Select() -- participants -- participants.ToDictionary() -- collection.Max() -- collection.Break() -- exit -- Max all -- -- Output ------------------------------ -- * OrderBy -- * Reverse -- * ThenBy -- * Select -- * Max -- participants -- * ToDictionary -- * Reverse -- * OrderBy -- * ThenBy -- * Select -- * Max -- collection -- * Break -- * Max -- -- -- Input ------------------------------- -- elements.Sort() -- elements.OrderBy() -- bound -- elements.Reverse().Select().ThenBy() -- keys.Reverse().OrderByDescending() -- keys.Reverse().ThenByDescending() -- 3 -- keys.Reverse().OrderBy().ThenBy() -- values.ToString().ToString().ThenBy() -- exit -- Reverse collection -- -- Output ------------------------------ -- * Sort -- * Select -- * ThenBy -- keys -- elements