I have a function that takes two filenames, and reads the contents of those two files into Strings, and then returns if they match or not. Here's the function:
f :: String -> String -> IO Bool
f fileName1 fileName2 = do
str1 <- readFile fileName1
str2 <- readFile fileName2
return (str1 == str2)
And if I use it like this from inside main:
main = do
res <- f "A.txt" "B.txt"
print res
It works and prints either True or False. What I want to do is, apply this function f to a list (of tuples) of filenames. For something like:
[("a.txt", "b.txt"), ("c.txt", "d.txt")]
(Assume that a.txt and b.txt have the same content and c.txt and d.txt are different).
I want to transform it (the list of file names) into a Bool list like: [True, False]. I tried using mapM, but that doesn't appear to map anything (when I print the list after using mapM, it prints the same list of tuples).
So my question is: What am I doing wrong, and how can I get a list of Bools like I mentioned above?
Please go easy on me as I'm still quite new to Haskell and functional programming :)
Here is a function f' which does what you describe.
f' :: [(String,String)] -> IO [Bool]
f' = mapM $ uncurry f
Let me know if something is unclear! And, just to be clear, here is how you run it:
main = do
res <- f' [("a.txt", "b.txt"), ("c.txt", "d.txt")]
print res
EDIT
The function is in pointfree form, so it is equivalent to f' lst = mapM (uncurry f) lst. mapM essentially maps each element of lst using f as the function, and pushes the IO to the outside of the list.
uncurry just takes a function of the form a -> b -> c and transforms it into one (a,b) -> c, which is what we want since you have a list of tuples.
Related
I am trying to write a function that takes two lists (list1 & list2) expressed as strings as arguments and after recursively iterating list1 and comparing a value from list2. when the value of list1 and list2 are equal, the recursion should break and return the two lists (modified).
For example. list1= "abcdef". list2= "def"
pseudo code:
for char in list1
if char==list2[0] --> [char:] list2[1:]
In the case above this would be returning: "def" "ef"
What I got so far:
isEqual :: String -> String -> String ->String
isEqual (s : os) (p : ps)
| p /= s = isEqual os (p : ps)
| otherwise = s:os ps
However, I get the following error message from vs-code:
• Couldn't match expected type ‘String -> String’ with actual type ‘[Char]’
Possible cause: ‘(:)’ is applied to too many arguments
In the expression: s : os ps In an
equation for ‘isEqual’:
isEqual (s : os) (p : ps)
| p /= s = isEqual os (p : ps)
| otherwise = s : os pstypecheck(-Wdeferred-type-errors)
Couldn't match expected type ‘[Char] -> [Char]’
with actual type ‘[Char]’
The function ‘os’ is applied to one argument, but its type ‘[Char]’ has none
In the second argument of ‘(:)’, namely ‘os ps’
In the expression: s : os pstypecheck(-Wdeferred-type-errors)
Your type signature won't work. The type signature:
isEqual :: String -> String -> String -> String
describes a function that takes three input strings and produces a single output string. In order to accept two input strings and produce two output strings, you need to write something like:
isEqual :: String -> String -> (String, String)
This function will return a pair or "tuple" of two strings. This is the standard way of returning two values, and you won't find a reasonable way to return the two strings without pairing them like this.
After this change, all you need to do is adjust your otherwise case to return a pair (s:os, ps):
isEqual :: String -> String -> (String, String)
isEqual (s : os) (p : ps)
| p /= s = isEqual os (p : ps)
| otherwise = (s:os, ps)
This functions appears to do what you want:
λ> isEqual "abcdef" "def"
("def","ef")
When it comes time to use the result of this function call in a larger program, you can either use functions fst and snd to extract the two lists from the returned pair, or you can use pattern matching to name the two lists. For example:
main = do
let (lst1, lst2) = isEqual "abcdef" "def"
putStrLn $ "First list was " ++ show lst1
putStrLn $ "Second list was " ++ show lst2
As per your follow-up comment, if you want to pass the two lists returned by isEqual to a function that takes the two lists as separate arguments, the normal way is via a let statement exactly as you discovered:
let (lst1, lst2) = isEqual (s:os) ps in isMatch ls1 lst2
Your first attempt will also work, but you need some additional parentheses to get it to parse right:
isMatch (fst (isEqual (s:os) ps)) (snd (isEqual (s:os) ps))
or even combining the two approaches:
let lsts = isEqual (s:os) ps in isMatch (fst lsts) (snd lsts)
There are a few other ways. The higher-order function uncurry "converts" a function that takes two arguments to one that takes a pair, so you can write:
(uncurry isMatch) (isEqual (s:os) ps)
^^^^^^^^^^^^^^^^^
`-- this function takes the pair directly
or, with the extraneous parentheses removed:
uncurry isMatch (isEqual (s:os) ps)
Again, these are just some alternatives. I think the solution you found with let is the clearest.
I want to process the data present in file "persons.txt".
But i have tried everything to process all the lines from text file.
The only way i can process data is by creating the list manually.
let myList = ["John";"23"]
I want the program to iterate through all the lines of the text file.
I have managed a way to pass all the content of the text file into a list but i can+t seem to move on from that stage.
My way of thinking is:
Read content from text file
Convert to OCaml list
Separate list into sublists
Iterate through sublists
Only print to screen text respecting conditions
Can you please guide me?
Thanks!!
open Printf
(* FILE CONTENTS *)
(*
John;23;
Mary;16;
Anne;21;
*)
let file = "data/persons.txt"
;;
(* READ FROM EXTERNAL FILE *)
let read_lines name : string list =
if Sys.file_exists (name) then
begin
let ic = open_in name in
try
let try_read () =
try Some (input_line ic) with End_of_file -> None in
let rec loop acc = match try_read () with
| Some s -> loop (s :: acc)
| None -> close_in_noerr ic; List.rev acc in
loop []
with e ->
close_in_noerr ic;
[]
end
else
[]
;;
(...)
Your question is not at all clear. Here are some observations:
First, your read_lines function doesn't return the input in the form you need.
What read_lines returns looks like this:
["John;23;"; "Mary;16;"; "Anne;21;"]
But what you want is something more like this:
[("John", "23)"; ("Mary", "16"); ("Anne", "21")]
The key here is to split the strings into pieces using ; as a separator. You can probably use String.split_on_char for this.
Second, you are not defining a function to calculate an answer from paramters. Instead your calculation is based on global variables. This won't generalize.
Instead of saying this:
let adult_check_condition =
... using global age and name ...
You need to define a function:
let adult_check_condition age name =
... use parameters age and name ...
Then you can call this function with different ages and names.
I am trying to print list with comma.
I have list like ["1","2","3"] and I want to print 1,2,3
How can I do that?
I tried:
printList xs = mapM_ (\(a) -> do
putStr a
putStr (",")) xs
But I dont know how to remove the last comma.
You can use intercalate. It'll insert the comma between each element of the list and concatenate the resulting list of strings to turn it into a single string.
import Data.List
toCommaSeparatedString :: [String] -> String
toCommaSeparatedString = intercalate ","
ghci> toCommaSeparatedString ["1","2","3"]
"1,2,3"
This is a bit of an XY problem: as Benjamin Hodgson shows, you’re better off turning your list into a string, and then printing that – you want as much of your logic outside of the IO monad as possible.
But of course, even if your question is somewhat in the wrong direction from the start, it has an answer! Which is that, for example, you could write this:
printList :: [String] -> IO ()
printList [] = return ()
printList [x] = putStr x
printList (x:xs) = do
putStr x
putStr ","
printList xs
Benjamin’s answer is better. But this one might elucidate IO monad code and do-notation a bit more.
I have 2 lists which I am trying to fill will items. While reading from stdin, depending on the value of one of the things read, I want to append to a different list. Example,
import Control.Monad(replicateM)
main = do
n <- getLine
let l1 = [], l2 = []
in replicateM (read n) (getLine >>= (\line ->
case line of "Yes" ->
-- do something with line
-- and append value of that thing to l1
"No" ->
-- do something else
-- append this value to l2
putStrLn line))
I realise the above code has syntax errors and such, but hopefully you can see what I am trying to and suggest something.
This is the answer I came up with
While we are at it, can someone explain why this gives me an infinite list:
let g = []
let g = 1:g
-- g now contains an infinite list of 1's
This is what I finally came up with:
import Control.Monad(replicateM)
import Data.Either
getEither::[String] -> [Either Double Double]
getEither [] = []
getEither (line:rest) = let [n, h] = words line
fn = read f :: Double
e = case heist of "Yes" -> Left fn
"No" -> Right fn
in e : getEither rest
main = do
n <- getLine
lines <- replicateM (read n) getLine
let tup = partitionEithers $ getEither lines :: ([Double], [Double])
print tup
Not sure how fmap could have been used in this instance
Here is a short ghci session that may give you some ideas:
> :m + Control.Monad Data.Either
> partitionEithers <$> replicateM 3 readLn :: IO ([Int], [Bool])
Left 5
Right True
Left 7
([5,7],[True])
The answer to your second question is that let is recursive; so the two gs in let g = 1:g are referring to the same in-memory object.
You are thinking in term of mutable variables: you are "initializing" l1,l2 to the empty list and then reasoning about updating them with longer lists. This design works fine in imperative programming, but not so simply in pure functional programming since it involves mutation.
Now, even in pure functional programming we have ways to simulate mutation, through monads. For instance, once can achieve mutation here through IORefs or StateT IO. In this case, though, is would be an unnecessarily complex way to solve the task.
You want to append data to form two lists. You want to use replicateM, which is fine. The point is that replicateM will build just one list, instead of two. The question now is: how can we create a list which is easily split into two?
A first ugly attempt is to generate a list of tagged values, i.e. a list of pairs:
case line of
"Yes" -> let value = ... in
return ("for l1", value)
"No" -> let value = ... in
return ("for l2", value)
Doing this would make replicateM produce a list such as
[("for l1", value1), ("for l1", value2), ("for l2", value3), ...]
which we can then split into two lists.
The use of strings for tags looks however a bit unelegant, since a boolean would suffice:
case line of
"Yes" -> let value = ... in
return (True, value)
"No" -> let value = ... in
return (False, value)
An even better approach would be to use the Either a b type:
case line of
"Yes" -> let value1 = ... in
return (Left value1)
"No" -> let value2 = ... in
return (Right value2)
The nice consequence of the above is that value1 and value2 can even be of different types. The previous snippets forced them to share their type: since we build a list of pairs each pair must have the same type. The new list is now instead of type [Either a b] where a is the type of values to be put in l1, and b that for l2.
Once you get a [Either a b] you want to split it in [a] and [b]. As #DanielWagner suggests in his answer, you can exploit partitionEithers for this.
I am trying to merge two sorted lists in Haskell. The two lists must contain the same types, but the function needs to take lists with different types.
This is what I have got(I know that I need some code to avoid trying to take elements out of empty lists as well):
merge :: Ord a => [a] -> [a] -> [a]
merge [] [] = []
merge (h:first) (c:second) | h <= c = h:merge first (c:second)
| h > c = c:merge (h:first) second
main = merge ['a','b','c'] ['d','e','f']
The thing is that I am new to Haskell and I get this error messege, that I kind of understand but don't know that to do about:
Couldn't match expected type `IO t0' with actual type `[Char]'
In the expression: main
When checking the type of the function `main'
Does anyone know what this means? Help is really appreciated!
main needs to be an IO action. If you want to print the list, do something like this:
main = print $ merge ['a','b','c'] ['d','e','f']
Note that your program does not run, because of "Non-exhaustive patterns in function merge" (that is, lists of length "1" are not considered). Moreover you can use "#" to make it more readable.
I'd rewrite it as:
merge :: Ord a => [a] -> [a] -> [a]
merge xs [] = xs
merge [] xs = xs
merge a#(h:first) b#(c:second)
| h <= c = h:merge first b
| h > c = c:merge a second
main = print $ merge ['a','b','d'] ['c','e','f']