Separating input string and call the required function using case statement - haskell
How can I separate the input string into two parts, first of which is a function and the second is the parameter. I want to call the function using the case statement in Haskell.
For example: I have a function sum which calculates the sum of the elements in a list and if a user writes sum 9 it must calculate the sum of the first 9 elements of the list.
First import Data.List, which has all the list manipulation functions.
Then to split at the first space, use span. Then drop the first element of the second half of the list, since that will be the space. The code is then:
import Data.List
f :: String -> (String, String)
f s = (func, tail args)
where (func, args) = span (/=' ') s
EDIT: Oh yeah, for the second part of your question. You're not adding functions to your list of callable functions at runtime right? So just make another function and pattern match on the first argument string, and call the appropriate function. This only works if your always returning an int though. I'm not sure how to make this work on generic return types Example:
eval :: (String, String) -> Int
eval ("sum", args) = sum (take (read . head $ args') (read . concat . tail $ words))
where args' = words args
Use words to split the string at the spaces.
You can then use a case expression and match on the function name and parameters.
If you only want to print the result you can work directly in the IO monad,
which allows you to work with functions of different return types:
evaluationLoop :: IO ()
evaluationLoop = do
input <- getLine
case words input of
"add" : xs -> (print $ sum $ map read xs) >> evaluationLoop
"concat" : xs -> (putStrLn $ concat xs) >> evaluationLoop
"exit" : [] -> return ()
You can then use it like this:
ghci> evaluationLoop
add 1 2 3 4 5
15
add -4 14
10
concat a hello
ahello
concat hi
hi
exit
ghci>
Related
Transforming a string into a list of pairs comprising the character from each run together with its number of repetitions
So I am required to convert a list of strings to a [(Char,Int)]. So for example, ["xxxxx","yyy"] to [('x',5), ('y',3)] . I am able to get the ('x',5) part without any issues but I am not sure how to move on to the next element of the list. Here is my code so far. Any pointers will be greatly appricated. [(x,y) | let x = head(head(reap xs)), let y = length(head(reap xs)))] p.s : reap is a function that turns a string into a list of repeated characters. For example "aaaabbbbccc" -> ["aaaa","bbbb","bbb"].
I suggest breaking this into smaller parts. First define a function that takes a single String and returns a tuple (Char, Int). Then once you have this function , you can use map to apply it to each String in a list.
You can use the fmap function which applies a function over or any item in the list. The function charRepetitions accepts a list and uses the charRepetition function to transform an item. main = do _ <- print $ charRepetitions ["xxxxx","yyy"] return () charRepetitions :: [String] -> [(Char, Int)] charRepetitions xs = fmap charRepition xs charRepetition :: String -> (Char, Int) charRepetition s = (head s , length s)
Project Euler 8 - I don't understand it
I looked up for a solution in Haskell for the 8th Euler problem, but I don't quite understand it. import Data.List import Data.Char euler_8 = do str <- readFile "number.txt" print . maximum . map product . foldr (zipWith (:)) (repeat []) . take 13 . tails . map (fromIntegral . digitToInt) . concat . lines $ str Here is the link for the solution and here you can find the task. Could anyone explain me the solution one by one?
Reading the data readFile reads the file "number.txt". If we put a small 16 digit number in a file called number.txt 7316 9698 8586 1254 Runing euler_8 = do str <- readFile "number.txt" print $ str Results in "7316\n9698\n8586\n1254" This string has extra newline characters in it. To remove them, the author splits the string into lines. euler_8 = do str <- readFile "number.txt" print . lines $ str The result no longer has any '\n' characters, but is a list of strings. ["7316","9698","8586","1254"] To turn this into a single string, the strings are concatenated together. euler_8 = do str <- readFile "number.txt" print . concat . lines $ str The concatenated string is a list of characters instead of a list of numbers "7316969885861254" Each character is converted into an Int by digitToInt then converted into an Integer by fromInteger. On 32 bit hardware using a full-sized Integer is important since the product of 13 digits could be larger than 2^31-1. This conversion is mapped onto each item in the list. euler_8 = do str <- readFile "number.txt" print . map (fromIntegral . digitToInt) . concat . lines $ str The resulting list is full of Integers. [7,3,1,6,9,6,9,8,8,5,8,6,1,2,5,4] Subsequences The author's next goal is to find all of the 13 digit runs in this list of integers. tails returns all of the sublists of a list, starting at any position and running till the end of the list. euler_8 = do str <- readFile "number.txt" print . tails . map (fromIntegral . digitToInt) . concat . lines $ str This results in 17 lists for our 16 digit example. (I've added formatting) [ [7,3,1,6,9,6,9,8,8,5,8,6,1,2,5,4], [3,1,6,9,6,9,8,8,5,8,6,1,2,5,4], [1,6,9,6,9,8,8,5,8,6,1,2,5,4], [6,9,6,9,8,8,5,8,6,1,2,5,4], [9,6,9,8,8,5,8,6,1,2,5,4], [6,9,8,8,5,8,6,1,2,5,4], [9,8,8,5,8,6,1,2,5,4], [8,8,5,8,6,1,2,5,4], [8,5,8,6,1,2,5,4], [5,8,6,1,2,5,4], [8,6,1,2,5,4], [6,1,2,5,4], [1,2,5,4], [2,5,4], [5,4], [4], [] ] The author is going to pull a trick where we rearrange these lists to read off 13 digit long sub lists. If we look at these lists left-aligned instead of right-aligned we can see the sub sequences running down each column. [ [7,3,1,6,9,6,9,8,8,5,8,6,1,2,5,4], [3,1,6,9,6,9,8,8,5,8,6,1,2,5,4], [1,6,9,6,9,8,8,5,8,6,1,2,5,4], [6,9,6,9,8,8,5,8,6,1,2,5,4], [9,6,9,8,8,5,8,6,1,2,5,4], [6,9,8,8,5,8,6,1,2,5,4], [9,8,8,5,8,6,1,2,5,4], [8,8,5,8,6,1,2,5,4], [8,5,8,6,1,2,5,4], [5,8,6,1,2,5,4], [8,6,1,2,5,4], [6,1,2,5,4], [1,2,5,4], [2,5,4], [5,4], [4], [] ] We only want these columns to be 13 digits long, so we only want to take the first 13 rows. [ [7,3,1,6,9,6,9,8,8,5,8,6,1,2,5,4], [3,1,6,9,6,9,8,8,5,8,6,1,2,5,4], [1,6,9,6,9,8,8,5,8,6,1,2,5,4], [6,9,6,9,8,8,5,8,6,1,2,5,4], [9,6,9,8,8,5,8,6,1,2,5,4], [6,9,8,8,5,8,6,1,2,5,4], [9,8,8,5,8,6,1,2,5,4], [8,8,5,8,6,1,2,5,4], [8,5,8,6,1,2,5,4], [5,8,6,1,2,5,4], [8,6,1,2,5,4], [6,1,2,5,4], [1,2,5,4] ] foldr (zipWith (:)) (repeat []) transposes a list of lists (explaining it belongs to perhaps another question). It discards the parts of the rows longer than the shortest row. euler_8 = do str <- readFile "number.txt" print . foldr (zipWith (:)) (repeat []) . take 13 . tails . map (fromIntegral . digitToInt) . concat . lines $ str We are now reading the sub-sequences across the lists as usual [ [7,3,1,6,9,6,9,8,8,5,8,6,1], [3,1,6,9,6,9,8,8,5,8,6,1,2], [1,6,9,6,9,8,8,5,8,6,1,2,5], [6,9,6,9,8,8,5,8,6,1,2,5,4] ] The problem We find the product of each of the sub-sequences by mapping product on to them. euler_8 = do str <- readFile "number.txt" print . map product . foldr (zipWith (:)) (repeat []) . take 13 . tails . map (fromIntegral . digitToInt) . concat . lines $ str This reduces the lists to a single number each [940584960,268738560,447897600,1791590400] From which we must find the maximum. euler_8 = do str <- readFile "number.txt" print . maximum . map product . foldr (zipWith (:)) (repeat []) . take 13 . tails . map (fromIntegral . digitToInt) . concat . lines $ str The answer is 1791590400
If you're not familiar with the functions used, the first thing you should do is examine the types of each function. Since this is function composition, you apply from inside out (i.e. operations occur right to left, bottom to top when reading). We can walk through this line by line. Starting from the last line, we'll first examine the types. :t str str :: String -- This is your input :t lines lines :: String -> [String] -- Turn a string into an array of strings splitting on new line :t concat concat :: [[a]] -> [a] -- Merge a list of lists into a single list (hint: type String = [Char]) Since type String = [Char] (so [String] is equivalent to [[Char]]), this line is converting the multi-line number into a single array of number characters. More precisely, it first creates an array of strings based on the full string. That is, one string per new line. It then merges all of these lines (now containing only number characters) into a single array of characters (or a single String). The next line takes this new String as input. Again, let's observe the types: :t digitToInt digitToInt :: Char -> Int -- Convert a digit char to an int :t fromIntegral fromIntegral :: (Num b, Integral a) => a -> b -- Convert integral to num type :t map map :: (a -> b) -> [a] -> [b] -- Perform a function on each element of the array :t tails tails :: [a] -> [[a]] -- Returns all final segments of input (see: http://hackage.haskell.org/package/base-4.8.0.0/docs/Data-List.html#v:tails) :t take take :: Int -> [a] -> [a] -- Return the first n values of the list If we apply these operations to our string current input, the first thing that happens is we map the composed function of (fromIntegral . digitToInt) over each character in our string. What this does is turn our string of digits into a list of number types. EDIT As pointed out below in the comments, the fromIntegral in this example is to prevent overflow on 32-bit integer types. Now that we have converted our string into actual numeric types, we start by running tails on this result. Since (by the problem statement) all values must be adjacent and we know that all of the integers are non-negative (by virtue of being places of a larger number), we take only the first 13 elements since we want to ensure our multiplication is groupings of 13 consecutive elements. How this works is difficult to understand without considering the next line. So, let's do a quick experiment. After converting our string into numeric types, we now have a big list of lists. This is actually kind of hard to think about what we actually have here. For sake of understanding, the contents of the list are not very important. What is important is its size. So let's take a look at an artificial example: (map length . take 13 . tails) [1..1000] [1000,999,998,997,996,995,994,993,992,991,990,989,988] You can see what we have here is a big list of 13 elements. Each element is a list of size 1000 (i.e. the full dataset) down to 988 in descending order. So this is what we currently have for input into the next line which is, arguably, the most difficult-- yet most important-- line to understand. Why understanding this is important should become clear as we walk through the next line. :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b -- Combine values into a single value :t zipWith zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] -- Generalization of zip :t (:) (:) :: a -> [a] -> [a] -- Cons operator. Add element to list :t repeat repeat :: a -> [a] -- Infinite list containing specified value Remember how I mentioned we had a list of 13 elements before (of varying-sized lists)? This is important now. The line is going to iterate over that list and apply (zipWith (:)) to it. The (repeat []) is such that each time zipWith is called on a subsequence, it starts with an empty list as its base. This allows us to construct a list of lists containing our adjacent subsequences of length 13. Finally, we get to the last line which is pretty easy. That said, we should still be mindful of our types :t product product :: Num a => [a] -> a -- Multiply all elements of a list together and return result :t maximum maximum :: Ord a => [a] -> a -- Return maximum element in the list The first thing we do is map the product function over each subsequence. When this has completed we end up with a list of numeric types (hey, we finally don't have a list of lists anymore!). These values are the products of each subsequence. Finally, we apply the maximum function which returns only the largest element in the list.
EDIT: I found out later what the foldr expression was for. (See comments bellow my answer). I think that this could be expressed in different way - You can simply add a guard at the end of the list. My verbose version of that solution would be: import Data.List import Data.Char euler_8 = do let len = 13 let str1 = "123456789\n123456789" -- Join lines let str2 = concat (lines str1) -- Transform the list of characters into a list of numbers let lst1 = map (fromIntegral . digitToInt) str2 -- EDIT: Add a guard at the end of list let lst2 = lst1 ++ [-1] -- Get all tails of the list of digits let lst3 = tails lst2 -- Get first 13 digits from each tail let lst4 = map (take len) lst3 -- Get a list of products let prod = map product lst4 -- Find max product let m = maximum prod print m
Reading numbers from input Haskell
I want to have a function that reads arbitrary int's until the number '0' is inserted, and then presents the numbers inserted in an ordered list. For that i wrote this function: import Data.List readIntegers :: IO() readIntegers = do putStrLn "insert a number: " num<-getLine let list = ordList ((read num :: Int):list) if (read num == 0) then print list else readIntegers where ordList ::[Int]->[Int] ordList [] = [] ordList xs = sort xs This compiles just fine, but when i insert the number '0', it gives me this error: *** Exception: <<loop>> What am i doing wrong ?
As #phg points out, you are essentially constructing an infinite list, and actually evaluating it causes the loop error. A simple implementation to resolve this issue is to define a helper function which takes an additional parameter - a list to store all the inputs read in from the screen, like so: readInteger :: IO () readInteger = readInteger' [] where readInteger' x = do putStrLn "insert a number: " num<-getLine if ((read num :: Int) == 0) then print $ ordList x else readInteger' $ (read num :: Int):x where ordList ::[Int]->[Int] ordList [] = [] ordList xs = sort xs Please note that the above is essentially just an implementation of #phg's answer, but with some changes to your original logic. Firstly, since 0 is a sentinel value, we shouldn't be appending that to our list. Second, we do not need to sort the list every single time we are adding a value to it. Sorting once at the time of printing/passing to another function is sufficient. Demo If you want to read an unspecified number of integers without prompting for user input and cut it off the moment you encounter 0, you would probably do well to use getContents, which will read everything from the standard input as a single string, lazily. Then, it is a simple matter of parsing it to a list of numbers and doing what you want with it, like so: readIntegers :: () readIntegers = do a <- getContents let b = ordList $ takeWhile (/= 0) $ map (\x -> read x :: Int) $ words a mapM (putStrLn . show) b where ordList ::[Int]->[Int] ordList [] = [] ordList xs = sort xs
let list = ordList ((read num :: Int):list) This is basically a recursive definition of a list of the form [x, x, ...] (like if you wrote an equation saying x = 1 + x). That is perfectly fine by itself, since Haskell is lazy; however, if you try to print list (aka "solve the equation"), it will fail, since it will try to print infinitely many numbers. You probably have a misconception about the workings of the (:) operator. Haskell functions will never perform an assignment operation and concatenate num onto list by changing it, like in imperative languages. There are only pure functions. If you want to accumulate all numbers, you should try to come up with a recursive definition of readIntegers, keeping its state (the list) in an additional parameter (there are also more sophisticated ways, hiding the state passing, but more complicated to use for a beginner).
For a more sophisticated solution, note that this is an unfold and you can use unfoldM from Control.Monad.Loops to implement it: import Control.Monad.Loops (unfoldM) readInts :: IO [Int] readInts = unfoldM $ fmap (check . read) getLine where check x = if x == 0 then Nothing else Just x This has the nice property that it returns the list in the order in which it was read.
Grab a string from a list and save it into another list?
I'm trying to grab a random item from a string list and save that into another string list but I can't get my code to work. import System.Random import Control.Applicative ( (<$>) ) food = ["meatballs and potoes","veggisoup","lasagna","pasta bolognese","steak and fries","salad","roasted chicken"] randomFood xs = do if (length xs - 1 ) > 0 then [list] <- (fmap (xs!!) $ randomRIO (0, length xs -1)) else putStrLn (show([list]) I'm getting parse error on input '<-' but I'm sure there are more issues then that. There is also the issue that the list may contain the same dishes two days in a row which is not what I want and I guess I can remove duplicates but that also would remove the number of items in the list which I want to stay the same as the number in the list. Anyone have a good idea how I could solve this? I have been searching for a day now and I can't find something useful for me but that's just because I'm looking in the wrong places. Any suggestion on how I can do this or where I can find the info will be greatly appreciated!
The reason it didn't work is that you needed another do after your if...then. (After a then you need an expression, not a pattern <- expression.) randomFood :: String -> IO () -- type signature: take a String and do some IO. randomFood xs = do if length xs > 1 then do [list] <- (fmap (xs!!) $ randomRIO (0, length xs -1)) else putStrLn (show([list]) But that still doesn't compile, because you don't actually do anything with your list. At the end of every do block, you need an expression to return. I think you meant to still print some stuff if the length of xs is too short, and you probably meant to print the selected food if there was more than one to choose from. Better would be: randomFood :: String -> IO () randomFood xs | length xs <= 1 = putStrLn $ show xs randomFood xs | otherwise = do item <- (xs!!) <$> randomRIO (0, length xs -1) putStrLn $ show(item) This | boolean test = syntax is better for conditional answers based on input. I changed [list] to item because you're selecting a single item randomly, not a list of items. Haskell is quite happy to let you put [list], because any string that's got one character in it matches [list]. For example, "h" = [list] if list='h', because "h" is short for ['h']. Any longer string will give you Pattern match failure. In particular, all the food you've specified has more than one character, so with this definition randomFood would never work! item will match anything returned by your randomRIO expression, so that's fine. You imported <$> then didn't use it, but it's a nice operator, so I've replaced fmap f iothing with f <$> iothing. I finally realised I'm doing the wrong thing with short lists; if I do randomFood ["lump of cheese"] I'll get ["lump of cheese"], which is inconsistent with randomFood ["lump of cheese"] which will give me "lump of cheese". I think we should separate the short list from the empty list, which enables us to do more pattern matching and less boolean stuff: randomFood :: String -> IO () randomFood [] = putStrLn "--No food listed, sorry.--" randomFood [oneitem] = putStrLn . show $ oneitem randomFood xs = do item <- (xs!!) <$> randomRIO (0, length xs -1) putStrLn . show $ item This gives three different definitions for randomFood depending on what the input looks like. Here I've also replaced putStrLn (show (item)) with putStrLn . show $ item - compose the functions show and putStrLn and apply ($) that to the item.
Few points to note : Don't intermix pure and impure code. Try to use library for a task rather than repeating what is already written. Here is the code using random-fu library import Data.Random import Control.Applicative food :: [String] food = ["meatballs and potoes","veggisoup","lasagna","pasta bolognese","steak and fries","salad","roasted chicken"] randomFood :: [String] -> RVar (Maybe String) randomFood [] = return Nothing randomFood xs = Just <$> randomElement xs main :: IO () main = (sample $ randomFood food) >>= print This is like choosing one element from a list randomly. > main Just "steak and fries" > main Just "meatballs and potoes" If you want to output just a random permutation of the above list, you can use shuffle like main = (sample $ shuffle food) >>= print Example > main ["meatballs and potoes","lasagna","steak and fries","roasted chicken","salad","pasta bolognese","veggisoup"] > main ["roasted chicken","veggisoup","pasta bolognese","lasagna","steak and fries","meatballs and potoes","salad"]
Shortening a String Haskell
How do you shorten a string in Haskell with a given number. Say: comp :: String -> String short :: String -> String chomp (x:xs) = (x : takeWhile (==x) xs) using comp I want to select a run of repeated characters from the start of a string, with the run comprising at most nine characters. For example: short "aaaavvvdd" would output "aaaa" and short "dddddddddd" outputs "ddddddddd". I know I need take but am not sure how to put that into the code. i've got this far but it doesn't work short x:xs | length(short x:xs) >9 = take(9) | otherwise = comp
The Quick Answer import Data.List short [] = [] short x = (take 9 . head . group) x This will give you output that matches your desired output. That is, *> short "aaaavvvdd" "aaaa" *> short "dddddddddd" "ddddddddd" Step by Step Development Use "group" to separate the items This solution depends on the "group" function in the Data.List library. We begin with the definition: short x = group x This gives us: *> short "aaaavvvddd" ["aaaa","vvv","ddd"] Use "head" to return only the first element Once we have the the elements in a list, we want only the first item of the list. We achieve this using "head": short x = (head . group) x "." is the Haskell function for function composition. It's the same as: short x = head (group x) or short x = head $ group x This will give us: *> short "aaaavvvdd" "aaaa" *> short "dddddddddddddd" "dddddddddddddd" Use "take" to get the first nine characters We finish the program by taking only the first nine characters of this result, and end up with our final function. To do this, we use the "take" function from the prelude: short x = (take 9 . head . group) x We now have the result that we wanted, but with one minor problem. Add another case to eliminate the error Note that using our current definition on the empty list causes an error, *> short "aaaavvvddd" "aaaa" *> short "" "*** Exception: Prelude.head: empty list Because "head" is undefined on the empty list, we need to handle another case: the empty list. Now we have: short [] = [] short x = (take 9 . head . group) x This is our "final answer".
Here is another version: short xs = take 9 $ takeWhile (== head xs) xs So we take from the list as long as the content equals the head of list (which is the first char of the string). Then we use take to shorten the result when necessary. Note that we don't need an additional case for empty strings, which is a consequence from Haskell's lazyness: If takeWhile sees that the list argument is empty, it doesn't bother to evaluate the condition argument, so head xs doesn't throw an error.
Here's a definition: import Data.List (group) short = take 9 . head . group Interestingly enough, since our returned string is a prefix of the original string, and is constrained to be at most 9 characters long, it doesn't matter whether we trim down to that limit first or last. So we could also use this definition: short = head . group . take 9 Both of these are written in the "pointfree" style which doesn't reference a lack of punctuation, but a lack of unnecessary variables. We could have also written the definition as short s = take 9 (head (group s)) Or, using $ to get rid of parentheses: short s = take 9 $ head $ group s The only other step is to extract only the first block of matching characters, which is what head . group does (equivalent to your chomp function). From the docs: group :: Eq a => [a] -> [[a]] The group function takes a list and returns a list of lists such that the concatenation of the result is equal to the argument. Moreover, each sublist in the result contains only equal elements. For example, group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"] It is a special case of groupBy, which allows the programmer to supply their own equality test.