Switching to ByteStrings - haskell

EDIT: I followed Yuras and Dave4420's advices (Thanks). I still have some errors. Updated the question. Finally I will use meiersi's version (Thanks) but I still want to find my errors...
I have a simple script that goes like this:
import System.Environment
getRow :: Int -> String -> String
getRow n = (!!n) . lines
getField :: Int -> String -> String
getField n = (!!n) . words'
words' :: String -> [String]
words' str = case str of
[] -> []
_ -> (takeHead " ; " str) : (words' (takeTail " ; " str))
takeHead :: String -> String -> String
takeHead st1 st2 = case st2 of
[] -> []
_ -> if st1 == (nHead (length st1) st2) then [] else (head st2):(takeHead st1 (tail st2))
takeTail :: String -> String -> String
takeTail st1 st2 = case st2 of
[] -> []
_ -> if st1 == (nHead (length st1) st2) then nTail (length st1) st2 else takeTail st1 (tail st2)
nTail :: Int -> String -> String
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (tail str)
in if (length str) < n then str else rec n str
nHead :: Int -> String -> String
nHead n str = let rec n str = if n == 0 then [] else (head str):(rec (n - 1) (tail str))
in if (length str) < n then str else rec n str
getValue :: String -> String -> String -> String
getValue row field src = getField (read field) $ getRow (read row) src
main :: IO ()
main = do
args <- getArgs
case args of
(path: opt1: opt2: _) -> do
src <- readFile path
putStrLn $ getValue opt1 opt2 src
(path: _) -> do
src <- readFile path
putStrLn $ show $ length $ lines src
It compiles and works. Then I wanted to switch to ByteStrings. Here is my attempt:
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.Char8 as Bc (cons, empty,unpack)
import qualified Data.ByteString.Lazy.UTF8 as Bu (lines)
import qualified System.Posix.Env.ByteString as Bg (getArgs)
separator :: B.ByteString
separator = (Bc.cons ' ' (Bc.cons ';' (Bc.cons ' ' Bc.empty)))
getRow :: Int -> B.ByteString -> B.ByteString
getRow n = (`B.index` n) $ Bu.lines
getCol :: Int -> B.ByteString -> B.ByteString
getCol n = (`B.index` n) $ wordsWithSeparator
wordsWithSeparator :: B.ByteString -> [B.ByteString]
wordsWithSeparator str = if B.null str then [] else (takeHead separator str):(wordsWithSeparator (takeTail separator str))
takeHead :: B.ByteString -> B.ByteString -> B.ByteString
takeHead st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then B.empty else B.cons (B.head st2) (takeHead st1 (B.tail st2))
takeTail :: B.ByteString -> B.ByteString -> B.ByteString
takeTail st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then nTail (toInteger (B.length st1)) st2 else takeTail st1 (B.tail st2)
nTail :: Integer -> B.ByteString -> B.ByteString
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (B.tail str)
in if (toInteger (B.length str)) < n then str else rec n str
nHead :: Integer -> B.ByteString -> B.ByteString
nHead n str = let rec n str = if n == 0 then B.empty else B.cons (B.head str)(rec (n - 1) (B.tail str))
in if (toInteger (B.length str)) < n then str else rec n str
getValue :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString
getValue row field = getCol (read (Bc.unpack field)) . getRow (read (Bc.unpack row))
main = do args <- Bg.getArgs
case (map (B.fromChunks . return) args) of
(path:opt1:opt2:_) -> do src <- B.readFile (Bc.unpack path)
B.putStrLn $ getValue opt1 opt2 src
(path:_) -> do src <- B.readFile (Bc.unpack path)
putStrLn $ show $ length $ Bu.lines src
It doesn't work. I could not debug it. Here is what GHC tells me:
BETA_getlow2.hs:10:23:
Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
In the second argument of `B.index', namely `n'
In the expression: (`B.index` n)
In the expression: (`B.index` n) $ Bu.lines
BETA_getlow2.hs:13:23:
Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
In the second argument of `B.index', namely `n'
In the expression: (`B.index` n)
In the expression: (`B.index` n) $ wordsWithSeparator
Any tips would be appreciated.

getRow n = (!!n) . lines
Compare with
getRow n = B.index . Bu.lines
In the second version you don't use n at all, so it is the same as
getRow _ = B.index . Bu.lines
In the fist example you use n as an argument to the (!!) operator. You need to do the same in the second version.
Looks like it is not the only issue in your code, but I hope it is a good point to start ;)

I'm taking the liberty to interpret the following two sub-questions into your original question.
What Haskell code would one typically write for a script like the one you posted.
What are the right data structures to efficiently perform the desired functionality.
The following code gives one answer to these two sub-questions. It uses the text library to represent sequences of Unicode characters. Moreover, it exploits the text library's high-level API to implement the desired functionality. This makes the code easier to grasp and thereby avoids potential mistakes in the implementation of low-level functions.
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as T
import qualified Data.Text.IO as T
import System.Environment (getArgs)
type Table a = [[a]]
-- | Split a text value into a text table.
toTable :: T.Text -> Table T.Text
toTable = map (T.splitOn " ; ") . T.lines
-- | Retrieve a cell from a table.
cell :: Int -> Int -> Table a -> a
cell row col = (!! col) . (!! row)
main :: IO ()
main = do
(path:rest) <- getArgs
src <- T.readFile path
case rest of
row : col : _ -> T.putStrLn $ cell (read row) (read col) $ toTable src
_ -> putStrLn $ show $ length $ T.lines src

The first two errors Yuras has resolved for you, I think.
Re the 3rd error:
words' :: B.ByteString -> [B.ByteString]
words' str = if B.null str then B.empty else ...
The B.empty should be []. B.empty :: B.ByteString, but the result is supposed to have type [B.ByteString].
Re the 4th-7th errors:
length :: [a] -> Int
B.length :: B.ByteString -> Int64
In this case I would change the type signatures of nTail and nHead to use Int64 instead of Int. If that didn't work, I'd use Integer on all Integral types, using toInteger to do the conversion.
Re the 8th error:
The input to read must be a String. There's no getting round that. You'll have to convert the B.ByteString to a String and pass that to read.
(Incidently, are you sure you want to switch to ByteString and not Text?)
Re the 9th (final) error:
args :: [Data.ByteString.ByteString] (n.b. a list of strict bytestrings, not the lazy bytestrings you use elsewhere) but in the pattern match you expect args :: B.ByteString for some reason.
You should pattern match on a [ByteString] the same way you pattern match on a [String]: they are both lists.
Convert args to something of type [B.ByteString] with map (B.fromChunks . return) args.

Related

How to change IO() into [String] in Haskell?

I am making a frame that print a character frame around a character as I show below.
This is example of frame. Example to understand what I actual frame is and What i am want from all these function right, left, up and down:
a is list of string that is A.
Main> showMatDownAttach '#' a
aaaaa
a a
a a
aaaaaaa
a a
a a
a a
#########
Main> showMatDownAttach '#' a
aaaaa
a a
a a
aaaaaaa
a a
a a
a a
#########
I also have for right and left. Now I want to make a function that can combine all of them in one function. How I can do this?
a = [" aaaaa "," a a "," a a "," aaaaaaa "," a a "," a a "," a a "]
--badar = putStr( concat (map (++ "\n")(letter 'a')))
--showMat :: Char -> IO()
--showMat ch = putStr (concat ( map(++ "\n") (letter 'a')))
replicateIt :: Int -> [Char] -> [Char]
replicateIt x ls=take x (cycle ls)
--repeatIt :: Int -> [Char] -> [[Char]]
repeatIt num []=[]
repeatIt num (x:sx)= replicateIt num [x]:(repeatIt num sx)
hStretchChar :: Int -> Char -> String
hStretchChar i ch = replicate i ch
hStretchString :: Int -> String -> String
hStretchString i sts = concat ( map ( hStretchChar i) sts)
hStretchListOfString :: Int -> [String] -> [String]
hStretchListOfString i stlist = map (hStretchString i ) stlist
vStretchString :: Int -> String -> String
vStretchString i str = concat (replicate i (str ++ "\n"))
vStretchListOfString :: Int -> [String] -> [String]
vStretchListOfString i strList = map (vStretchString i) strList
stretch :: Int -> Int -> [String] -> [String]
stretch i j strList = vStretchListOfString i (hStretchListOfString j strList)
showMat' :: [String] -> IO()
showMat' strList = putStr (concat (stretch 1 1 strList))
--Left Attach Character
leftattach :: Char -> [String] -> [String]
leftattach a strlist = map ( a: ) strlist
showMatCharAttachLeft :: Char -> [String] -> IO()
showMatCharAttachLeft a strList = putStr (concat (stretch 1 1 ( leftattach a strList)))
charToString :: Char -> String
charToString a = a:[]
--Right Attach Character
rightattach :: Char -> [String] -> [String]
rightattach a strlist = map (++(charToString a)) strlist
showMatCharAttachRight :: Char -> [String] -> IO()
showMatCharAttachRight a strList = putStr (concat (stretch 1 1 ( rightattach a strList)))
--Up Attach Character
upattach :: Char -> [String] -> [String]
upattach a strList = take (length (head strList)) (cycle (charToString a)) : strList
showMatUpAttach :: Char -> [String] -> IO()
showMatUpAttach a strList = putStr (concat (stretch 1 1 (upattach a strList)))
--Down Attach Character
downattach :: Char -> [String] -> [String]
downattach a strList = strList ++ listOfCharTolistOfString (take (length (head strList)) (cycle (charToString a)))
showMatDownAttach :: Char -> [String] -> IO()
showMatDownAttach a strList = putStr (concat (stretch 1 1 (downattach a strList)))
--test0 a strList = listOfCharTolistOfString (take (length (head strList)) (cycle (charToString a)))
listOfCharTolistOfString :: [Char] -> [String]
listOfCharTolistOfString a = a:[]
IO () is a very opaque type; there isn't much you can do with it, and there isn't any meaningful way to convert it to [String]. Once you're in IO, you can't get out of it.
Generally in Haskell you write most of your code without using IO. Here's a solution to the problem to demonstrate what I mean. Notice that all of the "frame" code is defined with pure functions, and IO doesn't get introduced until main at the very end.
import Data.Foldable (traverse_)
import Data.List (repeat)
frame1 :: a -> [a] -> [a]
frame1 f xs = [f] ++ xs ++ [f]
frame2 :: a -> [[a]] -> [[a]]
frame2 f grid = frame1 edge $ frame1 f <$> grid
where edge = take (width grid + 2) $ repeat f
width :: [[a]] -> Int
width [] = 0
width (x:_) = length x
a :: [[Char]]
a = [ " aaaaa "
, " a a "
, " a a "
, " aaaaaaa "
, " a a "
, " a a "
, " a a "
]
main :: IO ()
main = traverse_ putStrLn $ foldr frame2 a "* &"
Output:
***************
* *
* &&&&&&&&&&& *
* & aaaaa & *
* & a a & *
* & a a & *
* & aaaaaaa & *
* & a a & *
* & a a & *
* & a a & *
* &&&&&&&&&&& *
* *
***************
So, IO () encapsulates all side-effects and is not just about console output. So pedantically there is no way to "change IO () to [String]"
With that said, I believe System.Posix.Redirect is what you are looking for. You just want to call showMatDownAttach to capture stdout to get the [String] you want.
A quick Google search reveals a couple more packages to do this as well:
https://hackage.haskell.org/package/io-capture
https://hackage.haskell.org/package/silently
Now doing this is not very idiomatic Haskell and if you have access to showMatDownAttach you'll want to change it to something like what https://stackoverflow.com/a/40701235/111021 suggests. But since you ask this question I believe you already considered that and somehow that's not an option.

How to parametrize a constant (in this particular recursive function)?

test1 correctly produces the following structure from the string "abcdef":
(a,(1,[0])) -- type 'a' occur 1 time in position 0
(b,(1,[1])) -- type 'b' occur 1 time in position 1
(c,(1,[2]))
(d,(1,[3]))
(e,(1,[4]))
(f*,(1,[5])) -- type 'f' is the last of the list
But this result depends on the number 6, that is the length of a very particular class of string, invalid for general case.
So if the string in test1 is instead "abc" the result is wrong:
(a,(1,[0]))
(b,(1,[7]))
(c*,(1,[8]))
If the string in test1 is instead "abcdefgh" the result is also wrong:
(a,(1,[0]))
(b,(1,[2])) -- Should be [1]
(c,(1,[3])) -- Should be [2]
(d,(1,[4])) -- ...
(e,(1,[5]))
(f,(1,[6]))
(g,(1,[7]))
(h*,(1,[8]))
In addTrieWithCounter I'm not able to substitue this constant (6) with a parameterized function on the length of the word.
The CONTEXT of this function. The addTrieWithCounter will be placed in a special "loop" such "al alts" becames: addTrieWithCounter ... "al" 0 -> "drop the space" -> addTrieWithCounter ... "alts" 3. So the occurrences will be aligned with the initial string.
-- analyzing "all alts" should be obtained this result.
(a,(2,[4,0])) -- type 'a' occur 2 times in positions 3 and 0 (reversed order)
(l,(2,[5,1])) -- type 'l' (of seq "al") occur 2 times in positions 4 and 1 (reversed order)
(l*,(1,[2])) -- type 'l' (of seq "all") occur 1 time in positions 2
(t,(1,[6])) -- type 't' (of seq "alt") occur 1 time in positions 6
(s*,(1,[7])) -- type 's' (of seq "alts") occur 1 time in positions 7
It will be a trivial thing, but I have no idea.
Thanks in advance for your suggestions.
import qualified Data.Map as M
import Text.PrettyPrint as TP
import Data.Either (either)
data Trie a b = Nil | Trie (M.Map (Either a a) (b, Trie a b)) deriving Show
-- (Just a note: Trie will be a Monoid's instance. So with "Either" it is possible to distinguish the following cases: "all" and "alliance")
-- add an element to a Trie
addTrieWithCounter
:: Ord a =>
(Trie a (Int, [t1]), Int)
-> ((Int, [t1]) -> Int -> (Int, [t1]))
-> [a]
-> (Trie a (Int, [t1]), Int)
addTrieWithCounter (t,st) f [] = (t,st)
addTrieWithCounter (Nil,st) f xs = addTrieWithCounter (Trie M.empty, st) f xs
addTrieWithCounter (Trie m,st) f [x] =
(Trie $ M.insertWith (\(c,_) _ -> (f c st,Nil)) (Left x) (f (0,[]) st,Nil) m,st + 1)
addTrieWithCounter (Trie m, st) f (x:xs) =
case M.lookup (Right x) m of -- !!!!! PROBLEM IN THE FOLLOWING LINE !!!!!
Nothing -> let (t',st') = addTrieWithCounter (Nil, 6 - length xs ) f xs
in (Trie $ M.insert (Right x) (f (0,[]) st,t') m,st + 1)
Just (c,t) -> let (t',st') = addTrieWithCounter (t,st) f xs -- TO CHANGE
in (Trie $ M.insert (Right x) (f c st',t') m,st')
showTrieS f (t,_) = showTrie f t
showTrie :: Show a => (Either t t -> String) -> Trie t a -> Doc
showTrie _ Nil = empty
showTrie f (Trie m)
| M.null m = empty
| otherwise =
vcat $
do (k,(count,t)) <- M.assocs m
return $
vcat [ lparen TP.<> text (f k) TP.<> comma TP.<> (text . show $ count) TP.<> rparen
, nest 4 (showTrie f t)
]
test1 = showTrieS f1 t
where
f1 = (either (:"*") (:""))
t = addTrieWithCounter (Trie M.empty,0) f2 "abcdef"
f2 (cr,poss) st = ((cr + 1),(st : poss))
This will get you most of the way there. It doesn't solve your
exact problem, but shows how to remove the hard-coded length value.
import qualified Data.Map.Strict as M
import qualified Data.IntSet as S
import Data.Monoid
import Text.PrettyPrint hiding ((<>))
data GenTrie a b = Trie (M.Map a (b, GenTrie a b))
deriving (Show)
emptyTrie = Trie M.empty
data Info = Info { _count :: Int, _positions :: S.IntSet }
deriving (Show)
type Trie = GenTrie Char Info
addString :: Int -> String -> Trie -> Trie
addString i cs t = go t i cs
where
go :: Trie -> Int -> String -> Trie
go t i [] = t
go t i (c:cs) =
let Trie m = t
pair =
case M.lookup c m of
Nothing ->
let t2 = go emptyTrie (i+1) cs
val = Info 1 (S.singleton i)
in (val, t2)
Just (info,t1) ->
let t2 = go t1 (i+1) cs
val = info { _count = _count info+1
, _positions = S.insert i (_positions info)
}
in (val, t2)
in Trie (M.insert c pair m)
printTrie = putStrLn . showTrie
showTrie = render . trieToDoc
trieToDoc :: Trie -> Doc
trieToDoc (Trie m)
| M.null m = empty
| otherwise =
vcat $
do (ch, (info,t)) <- M.assocs m
let count = show (_count info)
pos = show (S.toList (_positions info))
return $
vcat [ text [ch] <> space <> text count <> space <> text pos
, nest 4 (trieToDoc t)
]
test1 = printTrie $ addString 0 "abc" emptyTrie
test2 = printTrie $ addString 4 "alts" $ addString 0 "all" emptyTrie
addTrieWithCounter (Trie m,st) f (x:xs) =
case M.lookup (Right x) m of
Nothing -> let (t',st') = addTrieWithCounter (Nil, st + 1 ) f xs
in (Trie $ M.insert (Right x) (f (0,[]) st,t') m, st')
Just (c,t) -> let (t',st') = addTrieWithCounter (t,st + 1) f xs
in (Trie $ M.insert (Right x) (f c st,t') m,st')

String vs Char mismatch in haskell

I'm getting an error about a type mismatch:
Main.hs:47:28:
Couldn't match type ‘[Char]’ with ‘Char’
Expected type: IO Char
Actual type: IO String
In the first argument of ‘liftIO’, namely ‘prompt’
In the second argument of ‘($)’, namely ‘liftIO prompt’
and struggling to understand why an IO Char is expected. Since prompt does type-check as IO String in line 46, and I thought, perhaps mistakenly, that liftIO would turn it to a String as suggested in this answer.
module Main where
import Syntax
import Parser
import Eval
import Pretty
import Counter
import Control.Monad
import Control.Monad.Trans
import System.Console.Haskeline
import Control.Monad.State
showStep :: (Int, Expr) -> IO ()
showStep (d, x) = putStrLn ((replicate d ' ') ++ "=> " ++ ppexpr x)
process :: Counter -> String -> InputT (StateT [String] IO) ()
process c line =
if ((length line) > 0)
then
if (head line) /= '%'
then do
modify (++ [line])
let res = parseExpr line
case res of
Left err -> outputStrLn $ show err
Right ex -> do
let (out, ~steps) = runEval ex
--mapM_ showStep steps
out_ps1 c $ out2iout $ show out
else do
let iout = handle_cmd line
out_ps1 c iout
-- TODO: don't increment counter for empty lines
else do
outputStrLn ""
out2iout :: String -> IO String
out2iout s = return s
out_ps1 :: Counter -> IO String -> InputT (StateT [String] IO) ()
out_ps1 c iout = do
--out <- liftIO iout
let out_count = c 0
let prompt = (getPrompt out_count iout) :: IO String
outputStrLn $ liftIO prompt
outputStrLn ""
getPrompt :: IO Int -> IO String -> IO String
getPrompt ion iout = do
n <- ion
out <- iout
return $ "Out[" ++ (show n) ++ "]: " ++ out
handle_cmd :: String -> IO String
handle_cmd line = if line == "%hist"
then
evalStateT getHist []
else
return "unknown cmd"
joinHist :: IO [String] -> IO String
joinHist ixs = do
xs <- ixs
return $ unlines xs
getHist :: StateT [String] IO String
getHist = do
hist <- lift get
let hists = (zip [(1::Int)..] hist) :: [(Int, String)]
return $ combineHist hists
combineHist :: [(Int, String)] -> String
combineHist hists = unlines $ map (\(i, h) -> show i ++ ": " ++ show h) hists
main :: IO ()
main = do
c <- makeCounter
repl c
repl :: Counter -> IO ()
repl c = evalStateT (runInputT defaultSettings(loop c)) []
loop :: Counter -> InputT (StateT [String] IO) ()
loop c = do
minput <- getLineIO $ in_ps1 $ c
case minput of
Nothing -> return ()
Just input -> process c input >> loop c
getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
s <- liftIO ios
getInputLine s
in_ps1 :: Counter -> IO String
in_ps1 c = do
let ion = c 1
n <- ion
let s = "Untyped: In[" ++ (show n) ++ "]> "
return s
More context can be found here.
You are passing an IO action in place of a String to outputStrLn. You should instead do:
prompt <- liftIO $ getPrompt out_count iout
outputStrLn prompt
to obtain the String from the IO action, using liftIO and then passing this to outputStrLn.

Generating sequence from Markov chain in Haskell

I would like to generate random sequences from a Markov chain. To generate the Markov chain I use the following code.
module Main where
import qualified Control.Monad.Random as R
import qualified Data.List as L
import qualified Data.Map as M
type TransitionMap = M.Map (String, String) Int
type MarkovChain = M.Map String [(String, Int)]
addTransition :: (String, String) -> TransitionMap -> TransitionMap
addTransition k = M.insertWith (+) k 1
fromTransitionMap :: TransitionMap -> MarkovChain
fromTransitionMap m =
M.fromList [(k, frequencies k) | k <- ks]
where ks = L.nub $ map fst $ M.keys m
frequencies a = map reduce $ filter (outboundFor a) $ M.toList m
outboundFor a k = fst (fst k) == a
reduce e = (snd (fst e), snd e)
After collecting the statistics and generating a Markov Chain object I would like to generate random sequences. I could imagine this method could look something like that (pseudo-code)
generateSequence mc s
| s == "." = s
| otherwise = s ++ " " ++ generateSequence mc s'
where s' = drawRandomlyFrom $ R.fromList $ mc ! s
I would greatly appreciate if someone could explain to me, how I should implement this function.
Edit
If anyone's interested it wasn't as difficult as I thought.
module Main where
import qualified Control.Monad.Random as R
import qualified Data.List as L
import qualified Data.Map as M
type TransitionMap = M.Map (String, String) Rational
type MarkovChain = M.Map String [(String, Rational)]
addTransition :: TransitionMap -> (String, String) -> TransitionMap
addTransition m k = M.insertWith (+) k 1 m
fromTransitionMap :: TransitionMap -> MarkovChain
fromTransitionMap m =
M.fromList [(k, frequencies k) | k <- ks]
where ks = L.nub $ map fst $ M.keys m
frequencies a = map reduce $ filter (outboundFor a) $ M.toList m
outboundFor a k = fst (fst k) == a
reduce e = (snd (fst e), snd e)
generateSequence :: (R.MonadRandom m) => MarkovChain -> String -> m String
generateSequence m s
| not (null s) && last s == '.' = return s
| otherwise = do
s' <- R.fromList $ m M.! s
ss <- generateSequence m s'
return $ if null s then ss else s ++ " " ++ ss
fromSample :: [String] -> MarkovChain
fromSample ss = fromTransitionMap $ foldl addTransition M.empty $ concatMap pairs ss
where pairs s = let ws = words s in zipWith (,) ("":ws) ws
sample :: [String]
sample = [ "I am a monster."
, "I am a rock star."
, "I want to go to Hawaii."
, "I want to eat a hamburger."
, "I have a really big headache."
, "Haskell is a fun language."
, "Go eat a big hamburger."
, "Markov chains are fun to use."
]
main = do
s <- generateSequence (fromSample sample) ""
print s
The only tiny annoyance is the fake "" starting node.
Not sure if this is what you're looking for. This compiles though:
generateSequence :: (R.MonadRandom m) => MarkovChain -> String -> m String
generateSequence mc s | s == "." = return s
| otherwise = do
s' <- R.fromList $ rationalize (mc M.! s)
s'' <- generateSequence mc s'
return $ s ++ " " ++ s''
rationalize :: [(String,Int)] -> [(String,Rational)]
rationalize = map (\(x,i) -> (x, toRational i))
All random number generation needs to happen in either the Random monad or the IO monad. For your purpose, it's probably easiest to understand how to do that in the IO monad, using evalRandIO. In the example below, getRandom is the function we want to use. Now getRandom operates in the Random monad, but we can use evalRandIO to lift it to the IO monad, like this:
main :: IO ()
main = do
x <- evalRandIO getRandom :: IO Double
putStrLn $ "Your random number is " ++ show x
Note: The reason we have to add the type signature to the line that binds x is because in this particular example there are no other hints to tell the compiler what type we want x to be. However, if we used x in some way that makes it clear that we want it to be a Double (e.g., multiplying by another Double), then the type signature wouldn't be necessary.
Using your MarkovChain type, for a current state you can trivially get the available transitions in the form [(nextState,probability)]. (I'm using the word "probability" loosely, it doesn't need to be a true probability; any numeric weight is fine). This is what fromList in Control.Monad.Random is designed for. Again, it operates in the Random monad, but we can use evalRandIO to lift it to the IO monad. Suppose transitions is your list of transitions, having the type [(nextState,probability)]. Then, in the IO monad you can call:
nextState <- evalRandIO $ fromList transitions
You might instead want to create your own function that operates in the Random monad, like this:
getRandomTransition :: RandomGen g => MarkovChain -> String -> Rand g String
getRandomTransition currState chain = do
let transitions = lookup currState chain
fromList transitions
Then you can call this function in the IO monad using evalRandIO, e.g.
nextState <- evalRandIO $ getRandomTransition chain

Use vector to manipulate Chars instead of lists

I have the some code that compile and works. And then some that don't.
My concern was that the first version was soooo bloated that it crashed while running on too big arguments, so I wrote a second version with performance in mind.
The second version does't even compile. Please advice.
import System.Environment (getArgs)
import Data.List (nub)
import System.Random
import Control.Applicative ( (<$>) )
import Control.Monad (replicateM)
randomItem :: [a] -> IO a
randomItem xs = (xs!!) <$> randomRIO (0, length xs - 1)
genFromMask :: [String] -> IO String
genFromMask = mapM randomItem
genMeSome :: [String] -> Int -> IO [String]
genMeSome mask n = do
glist <- replicateM (n*10) (genFromMask mask)
return $ take n $ nub glist
writeIt :: FilePath -> Int -> [String] -> IO ()
writeIt fi n mask = do
glist <- genMeSome mask n
writeFile fi $ unlines glist
maj :: String
maj = ['A'..'Z']
numa :: String
numa = ['0'..'9']
-- | Certaines regions n'utilisent aucune des plages libres
genBra :: [String]
genBra = ["VWXYZ",maj,maj," ",numa,numa,numa,numa]
genAus :: [String]
genAus = [maj,maj,maj," ",numa,numa,numa]
main :: IO ()
main = do
args <- getArgs
case args of
(mo:fi:n:_) -> case mo of
"aus" -> writeIt fi (read n) genAus
"bra" -> writeIt fi (read n) genBra
_ -> error "country is not supported"
_ -> error "wrong input, format is: genLicensePlate country file number"
And here is the second:
import System.Environment (getArgs)
import System.Random
import Crypto.Random.AESCtr (makeSystem)
import Control.Applicative ( (<$>) )
import qualified Data.Vector as V
import qualified Data.Text as T
import qualified Data.Text.IO as T
nubV :: V.Vector a -> V.Vector a
nubV va
| V.null va = V.empty
| V.any (== headV) tailV = nubV tailV
| otherwise = headV `V.cons` nubV tailV
where
headV = V.head va
tailV = V.tail va
randomItem :: RandomGen g => g -> V.Vector a -> (a,g)
randomItem g xs =
(xs V.! fst shamble, snd shamble)
where
shamble = randomR (0, V.length xs - 1) g
genFromMask :: RandomGen g => g -> V.Vector (V.Vector a) -> V.Vector a
genFromMask g xs =
if V.null xs
then V.empty
else fst paket `V.cons` genFromMask (snd paket) (V.tail xs)
where
paket = randomItem g (V.head xs)
genMeSome :: RandomGen g => g -> V.Vector (V.Vector a) -> Int -> V.Vector (V.Vector a)
genMeSome g mask n =
V.take n $ nubV $ V.replicateM (n*10) (genFromMask g mask)
writeIt :: RandomGen g => g -> FilePath -> Int -> V.Vector (V.Vector a) -> IO ()
writeIt g fi n mask =
T.writeFile fi $ T.unlines $ T.pack $ V.toList (V.map V.toList $ genMeSome g mask n)
maj = V.fromList ['A'..'Z']
num a = V.fromList ['0'..'9']
vspa = V.fromList " "
vtir = V.fromList "-"
-- | Certaines regions n'utilisent aucune des plages libres
genBra = V.fromList [static,maj,maj,vspa,numa,numa,numa,numa]
where
static = V.fromList "VWXYZ"
genAus = V.fromList [maj,maj,maj,vspa,numa,numa,numa]
main :: IO ()
main = do
g <- makeSystem
args <- getArgs
case args of
(mo:fi:n:_) -> case mo of
"aus" -> writeIt g fi (read n) genAus
"bra" -> writeIt g fi (read n) genBra
_ -> error "country is not supported"
_ -> error "wrong input, format is: genLicensePlate country file number"
I am trying to generate fake licenses plates, to populate an anonymous database.
EDIT1:
Here are the errors:
genLicensePlate.hs:22:12:
No instance for (Eq a)
arising from a use of `=='
In the first argument of `V.any', namely `(== headV)
In the expression: V.any (== headV) tailV
In a stmt of a pattern guard for
an equation for `nubV':
V.any (== headV) tailV
genLicensePlate.hs:48:52:
Couldn't match expected type `Char' with actual type
Expected type: V.Vector Char
Actual type: V.Vector [a]
In the first argument of `V.toList', namely
`(V.map V.toList $ genMeSome g mask n)'
In the second argument of `($)', namely
`V.toList (V.map V.toList $ genMeSome g mask n)'
EDIT2:
So the general idea is to use a mask to generate random Strings.
Like myFunc g [['A'..'Z'],['A'..'Z']] gives AA or ZZ or BA or FG etc...
Then I use this function to make a lot of those strings based on the mask.
After that I removes duplicate and take as many as needed (since I generate 10 times the number asked even with duplicate I am OK).
Finaly I drop it on a file.
I hope it is more clear.
Kind regards,
Sar
nubV needs an Eq constraint, since it compares elements (but you really should use a Set or HashSet or so to get a better algorithm)
nubV :: Eq a => V.Vector a -> V.Vector a
nubV va
| V.null va = V.empty
| V.any (== headV) tailV = nubV tailV
| otherwise = headV `V.cons` nubV tailV
where
headV = V.head va
tailV = V.tail va
And in writeIt, you lack a map,
writeIt :: RandomGen g => g -> FilePath -> Int -> V.Vector (V.Vector a) -> IO ()
writeIt g fi n mask =
T.writeFile fi $ T.unlines $ map T.pack $ V.toList (V.map V.toList $ genMeSome g mask n)
-- ^^^
since you get a list of lists of Char from V.toList (V.map V.toList $ genMeSome g mask n).
That fixes the two reported errors.

Resources