Sarfraz
Sarfraz

Reputation: 225

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

Upvotes: 1

Views: 202

Answers (1)

Daniel Fischer
Daniel Fischer

Reputation: 183873

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.

Upvotes: 3

Related Questions