I am trying to split a Data.Text into a [Text], run a function to the elements of that list, then unword them, or intercalate them. But the types do not match. Instead of a Text -> Text I get a [Text] -> [Text] function.
import Data.Text(Text, pack, unpack, intercalate, split)
f = split (\x-> x==' ') . intercalate (pack " ")
:t f
--Prints for some reason: f :: [Text] -> [Text]
g = split (\x-> x==' ') . Data.Text.unwords
:t g
-- Prints again: g :: [Text] -> [Text]
How can I get a Text -> Text function?
The type of your functions is:
split :: (Char -> Bool) -> Text -> [Text]
intercalate :: Text -> [Text] -> Text
If you were to write it without function composition the code would look like:
f :: Text -> Text
f text = intercalate (pack " ") (split (==' ') text)
-- or also: intercalate (pack " ") $ split (==' ') text
So what you want to do first is splitting and then intercalating. Look at how function composition is defined:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
It takes two functions f and g and returns a function f(g(x)) applying first g and then f. So, when you write
f = split (\x-> x==' ') . intercalate (pack " ")
what you would get is:
f :: [Text] -> [Text]
f = \x -> intercalate (pack " ") (split (==' ') x)
-- first splitting and then intercalating
The code you want is:
f :: Text -> Text
f = intercalate (pack " ") . split (== ' ')
-- equivalent to:
-- f = \x -> intercalate (pack " ") (split (== ' ') x)
-- where you first split and then intercalate
Related
I get text from .txt, process it. I get vowels and their number from the text. I cannot write tuple list [(Char, Int)] to text file. I want to make each line to have a letter and its number, but I can't write it at all.
`
import Data.List
import Char
add :: Eq a => a -> [(a, Int)] -> [(a, Int)]
add x [] = [(x, 1)]
add x ((y, n):rest) = if x == y
then (y, n+1) : rest
else (y, n) : add x rest
count :: Eq a => [a] -> [(a, Int)]
count = sortBy f . foldr add [] where
f (_, x) (_, y) = compare y x
ff x = filter (\x->elem (fst x) "aeyioAEYIO") x
fff x = ff (count x)
main :: IO ()
main = do
src <- readFile "input.txt"
writeFile "output.txt" (operate src)
operate :: [(Char, Int)] -> String
operate = fff
It gives out an error:
*** Term : operate
*** Type : [Char] -> [(Char,Int)]
*** Does not match : [(Char,Int)] -> String
operate type is wrong because fff has type [Char] -> [(Char, Int)]
operate :: [(Char, Int)] -> String
operate = fff
type inferenece suggests [Char] -> [(Char, Int)],
good
but return type still needs to be [(String, Int)] if we want to feed output of this function to formatOne
formatOne :: Show a => (String, a) -> String
formatOne (s, i) = s ++ " : " ++ show i
operate :: String -> [(String, Int)]
operate = map (\(x,y) -> (x:"", y)) . fff
-- (\(x,y) -> (x:"", y)) this lambda turns first element of tuple from Char to String
-- unlines joins elements of list into string while separating elements with \n
formatAll = unlines . map formatOne
main :: IO ()
main = do
src <- readFile "input.txt"
writeFile "output.txt" (formatAll (operate src))
I've got problem with a Haskell program.
I'm trying to change [[Char]] to [[Int]]
I've got
["2","2","1","2,2","1"]
list of char list
and I'm trying to change it to [[Int]]
[[2],[2],[1],[2,2],[1]]
I've tried
f :: [String] -> [Int]
f = map read
but it gives me
[2,2,1,*** Exception: Prelude.read: no parse
Can anybody help me with this?
The reason that this fails is because a string "2,2" can not be converted to an Int itself: this is a digit followed by a comma, followed by a digit. An Int is parsed by an optional minus sign, followed by some digits, and some extra possibilities like hexadecimal numbers, but let us ignore these for now.
The type signature you specify for f is however incorrect, based on the expected output. Your output type seems to be a list of lists of Ints, so [[Int]]. That means that you should specify f as:
f :: [String] -> [[Int]]
f = ...
We thus need to read every String to an [Int]. We can not use read directly here, since reading to an [Int] expects the string to start and end with square brackets. We can however add these manually like:
f :: [String] -> [[Int]]
f = map (\s -> read ('[' : s ++ "]"))
or a point-free version:
f :: [String] -> [[Int]]
f = map (read . ('[' :) . (++ "]"))
For example:
Prelude> f ["2","2","1","2,2","1"]
[[2],[2],[1],[2,2],[1]]
Towards safer parsing with readMaybe
Parsing from Strings like in the above way is of course not very "safe", since it is possible that the String does not follow the format. We can make this more safe and use for example readMaybe :: Read a => String -> Maybe a:
import Text.Read(readMaybe)
f :: [String] -> [Maybe [Int]]
f = map (readMaybe . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Just [2],Nothing,Just [4,7,3],Nothing]
we can omit the failed reads for example by using catMaybes :: [Maybe a] -> [a]:
import Data.Maybe(catMaybes)
import Text.Read(readMaybe)
f :: [String] -> [[Int]]
f = catMaybes . map (readMaybe . ('[' :) . (++ "]"))
For example:
Prelude Data.Maybe Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[[2],[4,7,3]]
or as #dfeuer said, we can use traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) to return an [[Int]] result wrapped in a Just if all parsing succeeded, and Nothing otherwise:
import Text.Read(readMaybe)
f :: [String] -> Maybe [[Int]]
f = traverse (readMaybe . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2","2","1","2,2","1"]
Just [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Nothing
Parse with error messages with readEither
We can obtain an error message wrapped in a Left in case the parsing fails by using readEither :: Read a => String -> Either String a:
import Text.Read(readEither)
f :: [String] -> [Either String [Int]]
f = map (readEither . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Right [2],Left "Prelude.read: no parse",Right [4,7,3],Left "Prelude.read: no parse"]
and use traverse in the same way to obtain an error message wrapped in a Left or the complete result in a Right:
import Text.Read(readEither)
f :: [String] -> Either String [[Int]]
f = traverse (readEither . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2","2","1","2,2","1"]
Right [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Left "Prelude.read: no parse"
Here, like #dfeuer says, it does not really shows much information. There are however parsers that can provide more informative parsing errors.
I have a list of tuples in the form:
[(String, Int)]
How can I print this to display like:
String : Int
String : Int
String : Int
...
I am very new to Haskell so please make it as clear as possible. Thank you!
Update: Here's how the code of my program now looks:
main = do
putStrLn "********* Haskell word frequency counter *********"
putStrLn ""
conts <- readFile "text.txt"
let lowConts = map toLower conts
let counted = countAllWords (lowConts)
let sorted = sortTuples (counted)
let reversed = reverse sorted
putStrLn "Word : Count"
mapM_ (printTuple) reversed
-- Counts all the words.
countAllWords :: String -> [(String, Int)]
countAllWords fileContents = wordsCount (toWords (noPunc fileContents))
-- Splits words and removes linking words.
toWords :: String -> [String]
toWords s = filter (\w -> w `notElem` ["and","the","for"]) (words s)
-- Remove punctuation from text String.
noPunc :: String -> String
noPunc xs = [ x | x <- xs, not (x `elem` ",.?!-:;\"\'") ]
-- Counts, how often each string in the given list appears.
wordsCount :: [String] -> [(String, Int)]
wordsCount xs = map (\xs -> (head xs, length xs)) . group . sort $ xs
-- Sort list in order of occurrences.
sortTuples :: [(String, Int)] -> [(String, Int)]
sortTuples sort = sortBy (comparing snd) sort
printTuple :: Show a => [(String, a)] -> IO ()
printTuple xs = forM_ xs (putStrLn . formatOne)
formatOne :: Show a => (String, a) -> String
formatOne (s,i) = s ++ " : " ++ show i
It returns this error to me:
fileToText.hs:18:28:
Couldn't match type ‘(String, Int)’ with ‘[(String, a0)]’
Expected type: [[(String, a0)]]
Actual type: [(String, Int)]
In the second argument of ‘mapM_’, namely ‘reversed’
In a stmt of a 'do' block: mapM_ (printTuple) reversed
Thanks for any help!
let's start by formatting one item:
formatOne :: Show a => (String, a) -> String
formatOne (s,i) = s ++ " : " ++ show i
now you can use this function (for example) with forM_ from Control.Monad to print it to the screen like this (forM_ because we want to be in the IO-Monad - because we are going to use putStrLn):
Prelude> let test = [("X1",4), ("X2",5)]
Prelude> import Control.Monad (forM_)
Prelude Control.Monad> forM_ test (putStrLn . formatOne)
X1 : 4
X2 : 5
in a file you would use it like this:
import Control.Monad (forM_)
printTuples :: Show a => [(String, a)] -> IO ()
printTuples xs = forM_ xs (putStrLn . formatOne)
formatOne :: Show a => (String, a) -> String
formatOne (s,i) = s ++ " : " ++ show i
compiling file
overall here is a version of your code that will at least compile (cannot test it without the text file ;) )
import Control.Monad (forM_)
import Data.Char (toLower)
import Data.List (sort, sortBy, group)
import Data.Ord (comparing)
main :: IO ()
main = do
putStrLn "********* Haskell word frequency counter *********"
putStrLn ""
conts <- readFile "text.txt"
let lowConts = map toLower conts
let counted = countAllWords lowConts
let sorted = sortTuples counted
let reversed = reverse sorted
putStrLn "Word : Count"
printTuples reversed
-- Counts all the words.
countAllWords :: String -> [(String, Int)]
countAllWords = wordsCount . toWords . noPunc
-- Splits words and removes linking words.
toWords :: String -> [String]
toWords = filter (\w -> w `notElem` ["and","the","for"]) . words
-- Remove punctuation from text String.
noPunc :: String -> String
noPunc xs = [ x | x <- xs, x `notElem` ",.?!-:;\"\'" ]
-- Counts, how often each string in the given list appears.
wordsCount :: [String] -> [(String, Int)]
wordsCount = map (\xs -> (head xs, length xs)) . group . sort
-- Sort list in order of occurrences.
sortTuples :: [(String, Int)] -> [(String, Int)]
sortTuples = sortBy $ comparing snd
-- print one tuple per line separated by " : "
printTuples :: Show a => [(String, a)] -> IO ()
printTuples = mapM_ (putStrLn . formatTuple)
where formatTuple (s,i) = s ++ " : " ++ show i
I also removed the compiler warnings and HLINTed it (but skipped the Control.Arrow stuff - I don't think head &&& length is more readable option here)
I can't wrap my head around where should I put parenthesis to get it working:
let read_lines filename =
let channel = open_in filename in
Std.input_list channel;;
let print_lines filename =
List.map print_string ((^) "\n") (read_lines filename);;
^ This is the closes I've got so far. If my terminology is vague: ((^) "\n") is what I call partial function (well, because it doesn't handle all of its arguments). print_string I call total function because... well, it handles all of its arguments.
Obviously, what I would like to happen is that:
List.map applies first ((^) "\n") to the element of the list.
List.map applies print_string to the result of #1.
How? :)
Maybe you want something like that?
# let ($) f g = fun x -> f(g x);;
val ( $ ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
# let f = print_string $ (fun s -> s^"\n");;
val f : string -> unit = <fun>
# List.iter f ["a";"b";"c";"d"];;
a
b
c
d
- : unit = ()
# let g = string_of_int $ ((+)1) $ int_of_string;;
val g : string -> string = <fun>
# g "1";;
- : string = "2"
Your code didn't work because missing parenthesis:
List.map print_string ((^) "\n") xs
is parsed as
(List.map print_string ((^) "\n")) xs
when you expected
List.map (print_string ((^) "\n")) xs
A few things: List.map is probably not what you want, since it will produce a list (of unit values) rather than just iterating. ((^) "\n") is probably also not what you want, as it prepends a newline, the "\n" being the first argument. (This is not a section as in Haskell, but a straightforward partial application.)
Here's a reasonable solution that is close to what (I think) you want:
let print_lines filename =
List.iter (fun str -> print_string (str ^ "\n")) (read_lines filename)
But I would rather write
let print_lines filename =
List.iter (Printf.printf "%s\n") (read_lines filename)
Which is both clearer and more efficient.
Is there a high-level API for doing search-and-replace with regexes in Haskell? In particular, I'm looking at the Text.Regex.TDFA or Text.Regex.Posix packages. I'd really like something of type:
f :: Regex -> (ResultInfo -> m String) -> String -> m String
so, for example, to replace "dog" with "cat" you could write
runIdentity . f "dog" (return . const "cat") -- :: String -> String
or do more advanced things with the monad, like counting occurrences, etc.
Haskell documentation for this is pretty lacking. Some low-level API notes are here.
How about the Text.Regex.subRegex in package regex-compat?
Prelude> import Text.Regex (mkRegex, subRegex)
Prelude> :t mkRegex
mkRegex :: String -> Regex
Prelude> :t subRegex
subRegex :: Regex -> String -> String -> String
Prelude> subRegex (mkRegex "foo") "foobar" "123"
"123bar"
I don't know of any existing function that creates this functionality, but I think that I'd end up using something like the AllMatches [] (MatchOffset, MatchLength) instance of RegexContent to simulate it:
replaceAll :: RegexLike r String => r -> (String -> String) -> String -> String
replaceAll re f s = start end
where (_, end, start) = foldl' go (0, s, id) $ getAllMatches $ match re s
go (ind,read,write) (off,len) =
let (skip, start) = splitAt (off - ind) read
(matched, remaining) = splitAt len matched
in (off + len, remaining, write . (skip++) . (f matched ++))
replaceAllM :: (Monad m, RegexLike r String) => r -> (String -> m String) -> String -> m String
replaceAllM re f s = do
let go (ind,read,write) (off,len) = do
let (skip, start) = splitAt (off - ind) read
let (matched, remaining) = splitAt len matched
replacement <- f matched
return (off + len, remaining, write . (skip++) . (replacement++))
(_, end, start) <- foldM go (0, s, return) $ getAllMatches $ match re s
start end
Based on #rampion's answer, but with the typo fixed so it doesn't just <<loop>>:
replaceAll :: Regex -> (String -> String) -> String -> String
replaceAll re f s = start end
where (_, end, start) = foldl' go (0, s, id) $ getAllMatches $ match re s
go (ind,read,write) (off,len) =
let (skip, start) = splitAt (off - ind) read
(matched, remaining) = splitAt len start
in (off + len, remaining, write . (skip++) . (f matched ++))
You can use replaceAll from the Data.Text.ICU.Replace module.
Prelude> :set -XOverloadedStrings
Prelude> import Data.Text.ICU.Replace
Prelude Data.Text.ICU.Replace> replaceAll "cat" "dog" "Bailey is a cat, and Max is a cat too."
"Bailey is a dog, and Max is a dog too."
maybe this approach fit you.
import Data.Array (elems)
import Text.Regex.TDFA ((=~), MatchArray)
replaceAll :: String -> String -> String -> String
replaceAll regex new_str str =
let parts = concat $ map elems $ (str =~ regex :: [MatchArray])
in foldl (replace' new_str) str (reverse parts)
where
replace' :: [a] -> [a] -> (Int, Int) -> [a]
replace' new list (shift, l) =
let (pre, post) = splitAt shift list
in pre ++ new ++ (drop l post)
For doing “search-and-replace” with “more advanced things with the monad, like counting occurrences, etc,” I recommend Replace.Megaparsec.streamEditT.
See the package README for specific examples of how to count occurrences.