count the occurence of a specific character of a file in haskell - haskell

I am a beginner with haskell and i wonder how i can count the characters in a file in haskell. From this book i wrote the count of any character in any string, but i wonder if i can do the same function with files. The code is similar like following;
count :: Char -> String -> Int
count x xs = length [x'|x'<-xs, x==x']
Any help would be very great. Thanks!
Edit: I am very new to haskell so this may be a very stupid question :)

By reusing your count function. You can map it over a readFile with returns you a file's content as a string:
count :: Eq a => a -> [a] -> Int
count x xs = length [x' | x' <- xs, x==x']
cntFile :: Char -> FilePath -> IO Int
cntFile c f = count c <$> readFile f
main :: IO ()
main = do
cnt <- cntFile 'c' "test.hs"
print cnt
The operator <$> is just the infix notation for fmap, which does to IO and any other Functor what map does to lists.
The longer alternative for beginner would probably be:
cntFile' :: Char -> FilePath -> IO Int
cntFile' c f = do
content <- readFile f
let cnt = count c content
return cnt

Related

How to replace multiple characters in a string in Haskell?

I am making a program that replaces stuff using the Esperanto X-System to Esperanto, so I need it to transform "cx" to "ĉ", "sx" to "ŝ", "gx" to "g", "jx" to "ĵ", and "ux" to "ŭ", and the same for uppercase letters.
Currently it converts "a" to "b", and "c" to "d". The method I am currently using will only work for replacing single character, not multiple characters. So how do I replace multiple characters (like "cx") instead of a single one (like "a")?
replaceChar :: Char -> Char
replaceChar char = case char of
'a' -> 'b'
'c' -> 'd'
_ -> char
xSistemo :: String -> String
xSistemo = map replaceChar
So currently "cats" will transform to "dbts".
As #AJFarmar pointed out, you are probably implementing Esperanto's X-system [wiki]. Here all items that are translated are digraphs that end with x, the x is not used in esperato itself. We can for example use explicit recursion for this:
xSistemo :: String -> String
xSistemo (x:'x':xs) = replaceChar x : xSistemo xs
xSistemo (x:xs) = x : xSistemo xs
xSistemo [] = []
where we have a function replaceChar :: Char -> Char, like:
replaceChar :: Char -> Char
replaceChar 's' = 'ŝ'
-- ...
This then yields:
Prelude> xSistemo "sxi"
"\349i"
Prelude> putStrLn (xSistemo "sxi")
ŝi
A generic method:
The problem looks similar to question 48571481.
So you could try to leverage the power of Haskell regular expressions.
Borrowing from question 48571481, you can use foldl to loop thru the various partial substitutions.
This code seems to work:
-- for stackoverflow question 57548358
-- about Esperanto diacritical characters
import qualified Text.Regex as R
esperantize :: [(String,String)] -> String -> String
esperantize substList st =
let substRegex = R.subRegex
replaceAllIn = foldl (\acc (k, v) -> substRegex (R.mkRegex k) acc v)
in
replaceAllIn st substList
esperSubstList1 = [("cx","ĉ"), ("sx","ŝ"), ("jx","ĵ"), ("ux","ŭ")]
esperantize1 :: String -> String
esperantize1 = esperantize esperSubstList1 -- just bind first argument
main = do
let sta = "abcxrsxdfuxoojxii"
putStrLn $ "st.a = " ++ sta
let ste = esperantize1 sta
putStrLn $ "st.e = " ++ ste
Program output:
st.a = abcxrsxdfuxoojxii
st.e = abĉrŝdfŭooĵii
We can shorten the code, and also optimize it a little bit by keeping the Regex objects around, like this:
import qualified Text.Regex as R
esperSubstList1_raw = [("cx","ĉ"), ("sx","ŝ"), ("jx","ĵ"), ("ux","ŭ")]
-- try to "compile" the substitution list into regex things as far as possible:
esperSubstList1 = map (\(sa, se) -> (R.mkRegex sa, se)) esperSubstList1_raw
-- use 'flip' as we want the input string to be the rightmost argument for
-- currying purposes:
applySubstitutionList :: [(R.Regex,String)] -> String -> String
applySubstitutionList = flip $ foldl (\acc (re, v) -> R.subRegex re acc v)
esperantize1 :: String -> String
esperantize1 = applySubstitutionList esperSubstList1 -- just bind first argument
main = do
let sta = "abcxrsxdfuxoojxiicxtt"
putStrLn $ "st.a = " ++ sta
let ste = esperantize1 sta
putStrLn $ "st.e = " ++ ste

Haskell - read list of persons

The problem sounds like this: write a program that reads a number n and then n persons, for each persons, read their name and age and then return the oldest persons/persons.
Example input:
3
Ion Ionel Ionescu
70
Gica Petrescu
99
Mustafa ben Muhamad
7
Example output
Oldest is Gica Petrescu (99 years).
My code so far:
readPers :: IO(String, Int)
readPers = do
name <- getLine
age <- readLn :: IO Int
return (name, age)
readPerss :: (Ord t, Num t) => t -> [IO (String, Int)]
readPerss n
| n > 0 = readPers : readPerss(n-1)
| otherwise = []
pFunc = do
print "Numer of persons:"
n <- readLn :: IO Int
let persons = readPerss n
return persons
I first read n, then I try to make a list of persons using readPers and readPerss, but I am stuck, I don't know how to tackle it from that point forward and I guess that my implementation thus far is not quite right.
How should I solve the problem?
You are very close! What you are doing in readPerss :: (Ord t, Num t) => t -> [IO (String, Int)] is returning a list of IO actions; each action returns a pair of String and Int when it is executed. Currently in pFunc you are only building this list of actions, storing it in a variable with let, and returning it from pFunc; you are never executing them with a <- “bind” statement.
There are a few simple ways to do what you want. The smallest change to your code that does what you want is to add sequence, which takes a container of actions and produces an action that returns a container:
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
Here t is [], m is IO, and a is (String, Int):
sequence :: [IO (String, Int)] -> IO [(String, Int)]
Another way is to rewrite readPerss so that it executes the actions directly, accumulating the (String, Int) results in a list instead of accumulating the IO actions:
readPerss :: (Ord t, Num t) => t -> IO [(String, Int)]
-- Change [IO …] to IO […]: ~~~~~~~~~~~~~~~~~~
readPerss n
| n > 0 = do
pers <- readPers
perss <- readPerss (n - 1)
return (pers : perss)
| otherwise = return []
I know you may not be supposed to use library functions if this is a homework assignment or exercise, but in typical code “repeat x action n times and accumulate the results” is often represented with replicateM n x:
replicateM :: Applicative m => Int -> m a -> m [a]
This is how I always do this (it is from a code challenge isn’t it). I always seperate IO and logic as soon as possible. Works perfect (unless N is very big).
import Data.List.Split (chunksOf)
type Person = (String, Int)
main = do
x <- getContents
putStrLn $ program x
program :: String -> String
program s = “Oldest is “ ++ x ++ “ (“ ++ (show y) ++ “ years old).”
where
(x, y) = solve persons
persons = [(name, read age :: Int) | [name, age] <- chunksOf 2 . tail . lines $ s]
solve :: [Person] -> Person
solve ls = undefined
I leave the undefined to you.

Repeat character a random number of times in Haskell

I'm trying to create a function for a silly IRC bot that will return a phrase where some of the letters are repeated a random number of times. The problem I'm having is that I can't find a way to use random numbers that ghc likes. It seems that even using this answer isn't being particularly helpful for getting my code to compile.
import System.Random
-- Write bad
baaad x y = "B" ++ (repeatA x) ++ "D " ++ (exclaim y)
-- StartHere
randomBad :: String
randomBad = do
x <- randomRIO(5,10) :: IO Int
y <- randomRIO(0,6) :: IO Int
return $ baaad x y
repeatA :: Int -> String
repeatA x = rptChr "A" x
exclaim :: Int -> String
exclaim x = rptChr "!" x
rptChr :: String -> Int -> String
rptChr x y = take y (cycle x)
Even with the trick of using a do block and passing the IO Ints to the function that way, I'm still getting compile errors that it found an IO Int when expecting Int.
randomBad is not in the IO monad.... It is type String, but you are defining it to be type IO String
Change this
randomBad :: String
to this
randomBad :: IO String
Then you should be able to use this in another IO action, like main:
main = do
theString <- randomBad
putStrLn theString

Prelude.read no parse. Input works in one function, not in another

Right, I have two functions. Both take exactly the same file input. run2D works perfectly, but oneR gives me the error Prelude.read: no parse. This confuses me as it's my understanding that the no parse error usually means there's a problem with the input file, which there obviously isn't.
run2D :: [String] -> IO()
run2D [file,r] = do
thefile <- readFile file
let [v,e,f] = lines thefile
print(pullChi(eg2D (verticesMake (read v)) (read e) (read f) (read r)) (read r))
oneR :: [String] -> IO()
oneR [file] = do
thefile <- readFile file
let [v,e,f] = lines thefile
print(oneRobot (read v) (read e) (read f))
Here's the contents of my input file
7
[[0,1],[1,2],[0,2],[1,3],[2,3],[1,4],[2,4],[0,6],[1,6],[1,5],[5,6],[4,5]]
[[0,1,2],[1,2,3],[1,2,4],[0,1,6],[1,5,6],[1,4,5]]
and my oneRobot function
oneRobot :: Integer -> [Integer] -> [Integer] -> Integer -- Takes #vertices, list of edges and robots and returns the euler characteristic where number of robots = 1
oneRobot v e f = v - genericLength(e) + genericLength(f)
The problem is: in your file, you have a representation of [[Integer]] at the second and the third line.
Change oneRobot function signature and implementation to reflect this:
oneRobot :: Integer -> [[Integer]] -> [[Integer]] -> Integer
or flatten your list of integer lists with concat if it fits your task:
print(oneRobot (read v) (concat $ read e) (concat $ read f))

How can I use map in this case

I have the code which create Data.Map:
import qualified Data.Map as Map
shift_string :: [Char] -> Int -> [Char]
shift_string s num = (drop num s) ++ (take num s)
ascii :: [Char]
ascii = ['a' .. 'z']
shifted_ascii :: Int -> [Char]
shifted_ascii n = shift_string ascii n
trans_table :: Int -> Map.Map Char Char
trans_table n = Map.fromList(zip ascii $ shifted_ascii n)
The 'trans_table' function returns the Map where one Char map to another map.
I can create the function to get one Char and return another one based on this Map:
translate_char :: Char -> Int -> Maybe Char
translate_char c n = Map.lookup c $ trans_table n
Now I want to 'translate' each symbol in the map. Something like this:
encode message key = map translate_char message
This code doesn't work as translate_function must have only one parameter. I need something like global variable to store the Map in it and lookup values from it in my map-function. But I don't know how to rewrite my code.
p.s. I guess I can just add 'key' to each char in my string but I am searching common solution.
I don't know whats key in your encode function but you can use something like
translate_char :: Int -> Char -> Maybe Char
translate_char n c = Map.lookup c $ trans_table n
encode :: Int -> String -> String
encode n s = catMaybes $ map (translate_char n) s
here n determines the number of characters you are rotating. I switched the order of parameters to avoid using flip.

Resources