I am not very experienced in Haskell so I am not too sure what's going on. I want to generate a list of random IP addresses and print them out but I keep encountering the errors below:
My code:
import System.Random (randomRIO)
import Data.List
import Control.Monad.Cont
main :: IO ()
main = do
let maxtests = 5
let mylist = createList maxtests []
forM_ mylist $ \ip -> do
print ip
createList :: Int -> [[Char]] -> [[Char]]
createList 0 mylist = return mylist
createList n mylist = do
myarr <- randomIp 4
let myip = (show (myarr !! 0)) ++ "." ++ (show (myarr !! 1)) ++ "." ++ (show (myarr !! 2)) ++ "." ++ (show (myarr !! 3))
let mylist2 = mylist ++ [myip]
let mylist3 = createList (n-1) mylist2
return mylist3
randomIp :: Int -> IO([Int])
randomIp 0 = return []
randomIp n = do
r <- randomRIO (0,255)
rs <- randomIp (n-1)
return (r:rs)
Error messages when I compile:
test2.hs:13:23: error:
* Couldn't match type `[Char]' with `Char'
Expected type: [[Char]]
Actual type: [[[Char]]]
* In the expression: return mylist
In an equation for `createList':
createList 0 mylist = return mylist
|
13 | createList 0 mylist = return mylist
| ^^^^^^^^^^^^^
test2.hs:15:14: error:
* Couldn't match type `IO' with `[]'
Expected type: [[Int]]
Actual type: IO [Int]
* In a stmt of a 'do' block: myarr <- randomIp 4
In the expression:
do myarr <- randomIp 4
let myip
= (show (myarr !! 0))
++
"."
++
(show (myarr !! 1))
++ "." ++ (show (myarr !! 2)) ++ "." ++ (show (myarr !! 3))
let mylist2 = mylist ++ ...
let mylist3 = createList (n - 1) mylist2
....
In an equation for `createList':
createList n mylist
= do myarr <- randomIp 4
let myip = ...
let mylist2 = ...
....
|
15 | myarr <- randomIp 4
| ^^^^^^^^^^
test2.hs:19:5: error:
* Couldn't match type `[Char]' with `Char'
Expected type: [[Char]]
Actual type: [[[Char]]]
* In a stmt of a 'do' block: return mylist3
In the expression:
do myarr <- randomIp 4
let myip
= (show (myarr !! 0))
++
"."
++
(show (myarr !! 1))
++ "." ++ (show (myarr !! 2)) ++ "." ++ (show (myarr !! 3))
let mylist2 = mylist ++ ...
let mylist3 = createList (n - 1) mylist2
....
In an equation for `createList':
createList n mylist
= do myarr <- randomIp 4
let myip = ...
let mylist2 = ...
....
|
19 | return mylist3
| ^^^^^^^^^^^^^^
Would appreciate any pointers on what I'm doing wrong.
Your problems all stem from not handling IO correctly.
First problem: createList calls randomIP, and randomIP's result is in IO. Thus, createList must have its result in IO too. So change createList :: Int -> [[Char]] -> [[Char]] to createList :: Int -> [[Char]] -> IO [[Char]].
Second problem: Since createList's result is in IO, the recursive call to itself must unwrap the IO. So change let mylist3 = createList (n-1) mylist2 to mylist3 <- createList (n-1) mylist2.
Third problem: Since createList's result is in IO, main's call to it must unwrap the IO. So change let mylist = createList maxtests [] to mylist <- createList maxtests [].
With all of those changes, it compiles.
Bonus material below here:
Also, note that you've unnecessarily reimplemented a few functions that are already built in to Haskell: replicateM, intercalate, and map.
To make use of intercalate and map, change let myip = (show (myarr !! 0)) ++ "." ++ (show (myarr !! 1)) ++ "." ++ (show (myarr !! 2)) ++ "." ++ (show (myarr !! 3)) to let myip = intercalate "." (map show myarr).
Making use of replicateM requires much larger changes, so I'll just show you the result:
import System.Random (randomRIO)
import Data.List
import Control.Monad.Cont
main :: IO ()
main = do
let maxtests = 5
mylist <- createList maxtests
forM_ mylist $ \ip -> do
print ip
createList :: Int -> IO [[Char]]
createList n = replicateM n $ do
myarr <- randomIp 4
let myip = intercalate "." (map show myarr)
return myip
randomIp :: Int -> IO [Int]
randomIp n = replicateM n $ do
r <- randomRIO (0,255)
return r
You can even continue the refactoring further if you want, to something like this:
import System.Random (randomRIO)
import Control.Monad
import Data.Foldable
import Data.List
main :: IO ()
main = do
let maxtests = 5
mylist <- createList maxtests
for_ mylist print
createList :: Int -> IO [[Char]]
createList n = replicateM n $ intercalate "." . map show <$> randomIp 4
randomIp :: Int -> IO [Int]
randomIp n = replicateM n $ randomRIO (0,255)
How to call IO () function inside another IO () function? I want to print to standard output then call function to do the same.
For example,
p :: String -> IO ()
p [x] = putStr x
p xs = q xs
q :: String -> IO ()
q (x:xs) = putStr x ++ p xs
Your first problem is with typing
p [x] = putStr x
{- putStr :: String -> IO ()
x :: Char, not a String
-}
and
q (x:xs) = putStr x ++ p xs
{- (++) :: [a] -> [a] -> [a]
putStr x :: IO (), not a list of anything.
-}
Let's look at q first, since it follows from p. You're breaking it down into characters, so you should use putChar rather than putStr
Also we're looking at sequencing actions, so we should either use (>>) or (>>=) depending on whether or not you need the result. In this case the result is a value of the unit type (()) which is a useless result and safe to ignore.
q :: String -> IO ()
q (x:xs) = putChar x >> p xs
{- or using `do` notation:
q (x:xs) = do
putChar x
p xs
-}
p can be changed likewise to use putChar rather than putStr
p :: String -> IO ()
p [x] = putChar x
p xs = q xs
though be aware that you haven't matched an empty list on either p or q.
About this time you should notice that substituting putChar for putStr just so you can break strings down to Chars is kind of backward thinking. p = putStr and you're done. However, if you're committed to this backward thinking:
import Control.Monad (foldM_, mapM_)
p = foldM_ (\_ x -> putChar x) ()
-- or
p = foldM_ ((putChar .) . flip const) ()
-- or
p = mapM_ putChar
My input is:
Plaster ["BD..", ".GA.D", ".FEG", "ABDCF", "E..."]
What I'm trying to get:
["BD..", ".GA.D", ".FEG", "ABDCF", "E..."]
My code for now:
go = do --print "Enter file name"
--path <- getLine
file <- (readFile "1.txt")
print file
let list = consume file
print list
let content = (wordsWhen (=='"') list)
print content
print (content !! 0)
print (content !! 1)
print (content !! 2)
wordsWhen :: (Char -> Bool) -> String -> [String]
wordsWhen p s = case dropWhile p s of
"" -> []
", " -> []
s' -> w : wordsWhen p s''
where (w, s'') = break p s'
consume [] = []
consume ('[':xs) = consume' xs
consume (_ :xs) = consume xs
consume' [] = []
consume' (']':xs) = []
consume' (x :xs) = x : consume' xs
So what I'm doing is
Read file from destination (now hardcoded for testing)
Get rid of the word "Plaster" with consume
Get all strings from file with wordsWhen
I tried different separators for wordsWhen, but I can't get what I need. In current form the output is:
"Plaster [\"BD..\", \".GA.D\", \".FEG\", \"ABDCF\", \"E...\"]"
"\"BD..\", \".GA.D\", \".FEG\", \"ABDCF\", \"E...\""
["BD..",", ",".GA.D",", ",".FEG",", ","ABDCF",", ","E..."]
"BD.."
", "
".GA.D"
Which is quite accurate, but I want to get rid of this words that contains only commas. I can change the separator to comma (which it's supposed to be, I think), but then output is given with all this slashes and quotation marks, like this:
"Plaster [\"BD..\", \".GA.D\", \".FEG\", \"ABDCF\", \"E...\"]"
"\"BD..\", \".GA.D\", \".FEG\", \"ABDCF\", \"E...\""
["\"BD..\""," \".GA.D\""," \".FEG\""," \"ABDCF\""," \"E...\""]
"\"BD..\""
" \".GA.D\""
" \".FEG\""
Is there a way to fix my code? Or should I do it in a different way?
EDIT: As it is my exercise, I can only use standard types and functions.
Well, you could cheat by defining a datatype with a Read instance that matches your existing input:
{-# OPTIONS_GHC -Wall -Werror -Wno-name-shadowing #-}
module Main where
data Input = Plaster [String] deriving (Read, Show)
main :: IO ()
main = do
Plaster xs <- readIO =<< readFile "1.txt"
_ <- traverse print (zip [0 :: Int ..] xs)
return ()
This works perfectly for me with ghc-8.0.2:
$ cat "1.txt"
Plaster ["BD..", ".GA.D", ".FEG", "ABDCF", "E..."]
$ ghc --make SO44269043.hs && ./SO44269043
(0,"BD..")
(1,".GA.D")
(2,".FEG")
(3,"ABDCF")
(4,"E...")
Alternately, you could define your own read instance:
data Input = Plaster [String]
instance Read Input where
readsPrec p = readParen (p >= 10) . runR $ do
Plaster <$> (string "Plaster" *> many1 whitespace *> R readList)
If you're unfamiliar with <$> and *>, it may be a little easier to read this as
readsPrec p = readParen (p >= 10) . runR $ do
_ <- string "Plaster"
_ <- many1 whitespace
xs <- R readList
return (Plaster xs)
Even without imports, it's not a lot of code to define a parser type R, basically just a wrapper that lets you define a monad instance for String -> [(a, String)]:
newtype R a = R { runR :: ReadS a }
instance Functor R where
fmap f = R . fmap (map (\(a, s) -> (f a, s))) . runR
instance Applicative R where
pure a = R $ \s -> [(a, s)]
mf <*> ma = R $ \s -> do
(f, s) <- runR mf s
(a, s) <- runR ma s
return (f a, s)
instance Monad R where
m >>= f = R $ \s -> do
(a, s) <- runR m s
runR (f a) s
The >>= (or bind) operator just means "parse some of the string
with the parser on the left, then parse the rest of the string
with the after passing the resulting value to the function on the right."
We get R readList :: R [String] for free now, so all we need to do
is skip the initial "Plaster" and any whitespace between that and the
list of strings:
string :: String -> R String
string = traverse char
many1 :: R a -> R [a]
many1 r = loop where
loop = (:) <$> r <*> (loop <|> return [])
whitespace :: R Char
whitespace = char ' ' <|> char '\t' <|> char '\n' <|> char '\r'
Normally we'd use <|> from GHC.Base, but it's not hard to define a one-off here. Basically
r <|> r' means "try to parse with r, and if it fails, try to parse with r' instead"
(<|>) :: R a -> R a -> R a
r <|> r' = R $ \s -> runR r s ++ runR r' s
And now all we need is the ability to match a single character:
char :: Char -> R Char
char c = R $ \s -> case s of
(c' : s) | c == c' -> [(c, s)]
_ -> []
If even using Prelude.readList is too easy, we can define our own parsers for lists and quoted strings:
readsPrec p = readParen (p >= 10) . runR $ do
Plaster <$> (string "Plaster" *> many1 whitespace *> listOf quotedString)
Where lists just have a leading '[', a trailing ']', and some number of delimited terms:
listOf :: R a -> R [a]
listOf term = char '[' *> (term `sepBy` string ", ") <* char ']'
sepBy :: R a -> R b -> R [a]
sepBy term delim = sepBy1 term delim <|> return []
sepBy1 :: R a -> R b -> R [a]
sepBy1 term delim = loop where
loop = (:) <$> term <*> ((delim *> loop) <|> return [])
Similarly, a quoted string just has a leading '"', a trailing '"', and some number of escaped characters:
quotedString :: R String
quotedString = char '"' *> many escapedChar <* char '"'
many :: R a -> R [a]
many r = many1 r <|> return []
escapedChar :: R Char
escapedChar = R $ \s -> case s of
'\\' : '\\' : s -> [('\\', s)]
'\\' : '"' : s -> [('"', s)]
c : s | c /= '\\' && c /= '"' -> [(c, s)]
_ -> []
It's worth noting the similarity between many/many1 and sepBy/sepBy1 - if we were really
lazy, we could define one in terms of the other:
many1 r = r `sepBy1` return ()
term `sepBy1` delim = (:) <$> term <*> many (delim *> term)
This is how it could be done:
import System.Environment
import System.IO
import Data.Maybe
import Text.Read
readStringList :: String -> Maybe [String]
readStringList = readMaybe
main = do --print "Enter file name"
handle <- openFile "1.txt" ReadMode
hSeek handle AbsoluteSeek 8
file <- hGetContents handle
let list = fromJust (readStringList file )
print list
let filterThis = "," :: String
let filtered = filter (/=filterThis) list
print filtered
To get first command line argument, use getArgs.
Here is a quick and dirty parser.
Be careful, it only works for well formed input, is not performant and code is not factorised. But there is no cheating ;)
Maybe it could give you some inspiration to solve your exercise.
plaster :: String -> String
plaster ('P':'l':'a':'s':'t':'e':'r':' ':xs) = xs
plaster s = undefined
brackets :: String -> String
brackets ('[':xs) = brackets xs
brackets (x:']':_) = [x]
brackets (x:xs) = x:brackets xs
quotes :: String -> String
quotes ('"':xs) = quotes xs
quotes (x:'"':_) = [x]
quotes (x:xs) = x:quotes xs
sepByComma :: String -> [String]
sepByComma s = go s ""
where
go [] acc = [acc]
go (',':' ':xs) acc = [acc] ++ go xs ""
go (x:xs) acc = go xs (acc ++ [x])
parse :: String -> [String]
parse s = map quotes . sepByComma . brackets . plaster $ s
Here is an alternative using only the very basics, no Monads, Functors or Applicative operators.
main :: IO()
main = do
input <- getLine
let output = parse input
print output
parse :: String -> [String]
parse = map stripQuotes . parse' . tokenize []
where
parse' :: [String] -> [String]
-- If the input matches the pattern, call parseList on the inner tokens.
-- Does not nest brackets! This is a simple regex match.
parse' ("Plaster":"[":tokens) | last tokens == "]" =
parseList [] (removeLast tokens)
parse' _ = error "The input does not have the form \"Plaster [...]\"."
parseList :: [String] -> [String] -> [String]
-- Empty list.
parseList tokens [] = tokens
-- Unexpected tokens.
parseList _ (",":_) = error "Unexpected comma."
parseList _ ("[":_) = error "No support for nested brackets."
parseList _ ("]":_) = error "Unexpected input after \"]\"."
-- One-element list.
parseList tokens [x] = tokens ++ [x]
-- Comma-separated list with at least two elements.
parseList tokens (x:",":y:ys) = parseList (tokens ++ [x]) (y:ys)
-- Comma at end of list, so we don’t want to give the "expected comma" error!
parseList _ [_,","] = error "Extra comma at end of list."
-- More than one element not separated by commas.
parseList _ (x:_) = error $ "Expected comma after \"" ++ x ++ "\"."
stripQuotes :: String -> String
stripQuotes ('"':xs) | last xs == '"' = removeLast xs
stripQuotes xs = error $ "Expected string literal instead of " ++ xs ++ "."
removeLast :: [a] -> [a]
removeLast xs = take ((length xs) - 1) xs
whitespace :: [Char]
whitespace = [' ', '\n', '\t'] -- Incomplete, but sufficient.
isWhitespace :: Char -> Bool
isWhitespace c = elem c whitespace
tokenize :: [String] -> String -> [String]
-- If we’ve consumed all the input, we’re done.
tokenize tokens [] = tokens
-- We’d need something a little more complicated for longer operators:
tokenize tokens ('[':xs) = tokenize (tokens ++ ["["]) xs
tokenize tokens (']':xs) = tokenize (tokens ++ ["]"]) xs
tokenize tokens (',':xs) = tokenize (tokens ++ [","]) xs
-- Not currently processing a token, so skip whitespace.
-- Otherwise, start a new token.
tokenize tokens (x:xs) | isWhitespace x = tokenize tokens xs
| otherwise = tokenize' tokens [x] xs
where
tokenize' :: [String] -> String -> String -> [String]
-- If we’ve consumed all the input, the current token is the last.
tokenize' ts t [] = ts ++ [t]
-- If we encounter an operator, it is the token after the current one.
tokenize' ts t ('[':ys) = tokenize (ts ++ [t] ++ ["["]) ys
tokenize' ts t (']':ys) = tokenize (ts ++ [t] ++ ["]"]) ys
tokenize' ts t (',':ys) = tokenize (ts ++ [t] ++ [","]) ys
-- Whitespace means the current token is complete.
-- Otherwise, append y to the current token and continue.
tokenize' ts t (y:ys) | isWhitespace y = tokenize (ts ++ [t]) ys
| otherwise = tokenize' ts (t ++ [y]) ys
You wouldn’t do this in production code; this is simple enough to do with a regex, and parsing is (more or less) a solved problem. Parser combinators are the trendy way to go.
I want to know how to convert a string to lowercase using the ToLower function (Char -> Char).
This is the code I have so far:
let newlist = (\(h:t) -> c (h) ++ newlist (\c -> toLower c)
I can't see how to do it without using recursion, which I don't know how to use in a lambda expression
It would be easier to not use a lambda expression considering you can eta-reduce to not explicitly name the variable your function accepts. For example you could use a list comprehension:
import Data.Char
lowerString str = [ toLower loweredString | loweredString <- str]
Which you would call as:
ghci> lowerString "Hello"
hello
Alternatively, you could use map:
lowerString = map toLower
If you insist on using a lambda expression, it would look something like this:
import Data.Char
lowerString = \x -> map toLower x
Which again, is not as nice.
With a lambda expression you'd still need to recursively check each character of the string, or you could use map which is still a recursive function that applies the function (a -> b) to every element in the list, for example:
newList [] = []
newList xs = (\(y:ys) -> if x `elem` ['A'..'Z'] then toLower y : newList ys else y : newList ys) xs
With map is actually much simpler due to reasons explained in the first paragraph, check mnoronha's answer as he already gave you the answer, but that's if you're thinking of using map in conjunction with toLower.
This is an example without a lambda expression, which requires you to import 2 functions from Data.Char, recursively check the rest of the string and replace each character with its lower case version.
newList :: String -> String
newList [] = []
newList (x:xs) = if x `elem` ['A'..'Z']
then chr (ord x + 32) : newList xs
else x : newList xs
or with guards
newList :: String -> String
newList [] = []
newList (x:xs)
| x `elem` ['A'..'Z'] = chr (ord x + 32) : newList xs
| otherwise = x : newList xs
This may be more trouble than it's worth but you can use the fix function to recursively call a lambda function.
fix :: (a -> a) -> a
fix f = let x = f x in x
The following code uses the same method used on this page : https://www.vex.net/~trebla/haskell/fix.xhtml to transition from a non-lambda version to a lambda version to (eventually) using fix to recursively call a lambda function without introducing a new variable
import Data.Char (toLower)
import Control.Monad.Fix (fix)
main :: IO ()
main = do
-- calling non lambda version
let newlist1 s = if null s then [] else toLower (head s) : newlist1 (tail s)
print $ newlist1 "Hello"
-- calling lambda version
let newlist2 = \s -> if null s then [] else toLower (head s) : newlist2 (tail s)
print $ newlist2 "Hello"
-- defining lambda version locally
-- ( at this point the scope of newlist3 is local to the let statement )
print $ (let newlist3 = \s -> if null s then [] else toLower (head s) : newlist3 (tail s) in newlist3 ) "Hello"
-- making function an argument to be called recursively
print $ (let newlist3 = (\v -> \s -> if null s then [] else toLower (head s) : v (tail s)) newlist3 in newlist3 ) "Hello"
-- isolating function to be "fixed".
let f = (\v -> \s -> if null s then [] else toLower (head s) : v (tail s))
print $ (let newlist3 = f newlist3 in newlist3 ) "Hello"
-- using fix = let x = f x in x
print $ (fix f) "Hello"
-- f2 is slightly simpler version of f
let f2 = (\v s -> if null s then [] else toLower (head s) : v (tail s))
print $ (fix f2) "Hello"
-- inlining f2 to get recursive call to lambda
print $ (fix (\v s -> if null s then [] else toLower (head s) : v (tail s))) "Hello"