Compare two files in Erlang - file

I have two different files. First file look like this:
hallo
bye
something
new
And in second file I have eg. bye. From name of second file (eg. msg-0002) I know that this message must be second in first file. How can I, in Erlang, find that particularly word in first file? I just have to see if the second word is really bye.
That's the code I made so far:
-module(compare).
-export([compare/0]).
compare () ->
{ok, Pars} = file:read_file("words.txt"),
{ok, Dump} = file:read_file("msg-0002.file"),
L1 = binary:split(Pars, <<"\n">>, [global]).
L2 = binary:split(Dump, <<"\n">>, [global]).
In this code I get all the words in lists. I don't know how to get from name of msg-0002 file that word must be in 2. place in first file. And how to check if this particularly word is really in eg. second place in first file? That's important.

to extract the line number from the file name (assuming the file names are always of the form "msg-XXX.file":
FileName = "msg-0002.file",
{ok,[NumLine],_} = io_lib:fread("msg-~d.file",FileName),
and then to check if the 2 files are consistent, use Dogbert proposal:
{ok, Pars} = file:read_file("words.txt"),
{ok, Dump} = file:read_file(FileName),
L1 = binary:split(Pars, <<"\n">>, [global]),
L2 = binary:split(Dump, <<"\n">>, [global]),
Check = lists:nth(NumLine, L1) == lists:nth(1, L2),
...

There are many ways how you can do it, for example in escript:
#!/usr/bin/env escript
main(["-q"|ARGS]) ->
compare(ARGS, true);
main(ARGS) ->
compare(ARGS, false).
compare([MsgFile, WordsFile], Quiet) ->
case io_lib:fread("msg-~d", MsgFile) of
{ok, [N], _} when N > 0 ->
Msg = read_msg(MsgFile),
Word = read_nth(WordsFile, N),
case Msg =:= Word of
true ->
Quiet orelse io:put_chars("Yes\n"),
halt(0);
false ->
Quiet orelse io:put_chars("No\n"),
halt(1)
end;
_ -> usage()
end;
compare(_, _) ->
usage().
read_msg(File) ->
{ok, FH} = file:open(File, [read, binary]),
{ok, Msg} = file:read_line(FH),
ok = file:close(FH),
Msg.
read_nth(File, N) ->
{ok, FH} = file:open(File, [raw, read, binary, read_ahead]),
Word = read_nth_(FH, N),
ok = file:close(FH),
Word.
read_nth_(FH, N) ->
case file:read_line(FH) of
{ok, Word} when N =:= 1 -> Word;
{ok, _} -> read_nth_(FH, N-1);
Error -> error({words_file, Error})
end.
usage() ->
io:format(standard_error, "~ts [-q] <msg-file> <words-file>~n"
"\t<msg-file> - must have name in form msg-N*~n",
[escript:script_name()]),
halt(255).

Related

Reading integers from file OCaml

I have to read integers from a "file.txt" in OCaml and store them in a list. I have tried to read with stdlib but it does not work. Also I cannot understand how scanf works for files. If someone could help me with scanf i would be grateful.
Edit
Sorry for not being clear enough, first time posting. The input format is a b c\n
d e f\n
...
Yes it is for my university. Basically i have to find mst of a graph. The input is vertex_1 vertex_2 weight \n and so on and i am trying to build a list of tuples [(vertex_1 vertex_2 weight),...] from input. In my code i am trying to gather chars to a string if its needed (ex two digit numbers) and then converting string to int. But i hope there is an easier way of doing that. I have to say that is the second day that i am programming in ocaml.
let entry_of_channel ch =
let number = input_char ch in number
let rec list_of_channel ch =
try
let e = entry_of_channel ch in
e:: list_of_channel ch
with
End_of_file -> []
let string_of_chars chars =
let buf = Buffer.create 16 in
List.iter(Buffer.add_char buf) chars
let rec list_clear list buffer =
match list with
[] -> []
|' '::t -> (string_of_chars buffer)::list_clear t []
|'\n'::t ->(string_of_chars buffer)::list_clear t []
|h::t -> buffer # h;
list_clear t buffer
let graph filename =
let ch = open_in filename in
let l = list_of_channel ch in
close_in ch;
let l_new = list_clear l [] in
l_new
Since this is presumably for a school assignment, you should ideally show some code you've written and ask for help with a specific problem. However, reading integers from a file is probably not the interesting part of the assignment.
You don't give any information about the format of the file. Here's a function that reads whatever integers it finds on lines in a file, separated by spaces and tabs:
let read_ints filename =
let inchan = open_in filename in
let spre = Str.regexp "[ \t]+" in
let rec loop accum =
match input_line inchan with
| line ->
let wds = Str.split spre line in
loop (List.rev (List.map int_of_string wds) # accum)
| exception End_of_file ->
close_in inchan;
List.rev accum
in
loop []
Note that this does not handle errors. If the file can't be opened, or if there are non-integer values in the file, the code will raise an exception.
Here is a similar function that uses Scanf.fscanf:
let read_ints filename =
let inchan = open_in filename in
let rec loop accum =
match Scanf.fscanf inchan " %d" Fun.id with
| n -> loop (n :: accum)
| exception End_of_file ->
close_in inchan;
List.rev accum
in
loop []

Try/catch in a game loop

I have this little guessing game code. In order to deal with the string input, I used try/catch blocks. Try works perfectly, but catch-block is outside of the loop and I can't seem to make it work inside. So the program stops after catching an exception. What should I do so that my loop continues after catching an exception?
fun main(args: Array<String>) {
val rand = java.util.Random()
val n = 1 + rand.nextInt(100)
var guess: Int
var numberOfTries = 0
println("I guessed a number from 1 до 100. What is it?\n")
try {
do {
guess = readLine()!!.toInt()
var x = Math.abs(n - guess)
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
}
} while (guess != n)
} catch (e: NumberFormatException) {
println("Use digits, please!") }
println("Wow! You only used $numberOfTries tries!")
}
As MFazio23 mentioned, you use the try inside the while loop. Otherwise, it will exit the loop if an exception is thrown.
If an exception is thrown, anything inside is halted, which includes further code. if you have a method that throws an exception, no code after it will be executed. The try-catch creates an entry-point for the exception; your code will continue inside the relevant catch block (or exit the program if there is none), which means the loop inside the try-catch will stop.
However, you actually don't need the try-catch at all. Kotlin has a nice extension function called toIntOrNull, which does exactly what you'd expect; it attempts to convert the input to an int, and returns the number, or null if it failed. So, you can do this:
fun main(args: Array<String>) {
val rand = java.util.Random()
val n = 1 + rand.nextInt(100)
var guess: Int?
var numberOfTries = 0
println("I guessed a number from 1 до 100. What is it?\n")
do {
guess = readLine()?.toIntOrNull() // Note that this now uses ?. instead of !!. This is to make the null check useful If it throws an NPE, it kinda defeats the point. If the line is null, it now prints the same message as an invalid number
// numberOfTries++ // move this up here if you want to count invalid guesses as a guess
if(guess == null){
System.out.println("Only use numbers")
continue;
}
val x = Math.abs(n - guess)// I also changed this to a val; it's immutable, so it doesn't need to be a var
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
}
} while (guess != n)
println("Wow! You only used $numberOfTries tries!")
}
You can also optimize it further, but using an extension function/variable (I'm not sure what it is, it's a variable declared as an extension function, but since there's a getter too, I'm not sure what to call it) called absoluteValue.
You could also use if-statements, but it is slightly more boilerplate than using this. You cannot call Math.abs with null, because it uses primitives. Primitives in Java can never be null.
Which means anything you pass you the method cannot be null, which in Kotlin means for an instance Int. If it's nullable, it's an Int?, but the method requires non-null from Kotlin. You can't pass Int? to Int (you can do it the other way around, but that's not relevant here).
Under the hood, .absoluteValue calls Math.abs(n), but since it's a call, you can use the null-safe operator (?.)
guess = readLine()?.toIntOrNull()
val x = guess?.absoluteValue
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
null -> println("Please only use numbers")
}
And now that x is nullable, you can add null to the when statement (in response to your comment).
Also, if you only want numberOfTries to increment on valid numbers, add an if(x != null) before you call it.
You should be able to add the try...catch block right in your do...while. The only other change needed would be to initialize guess with a value (since it's not guaranteed to be set before the while block is hit):
val rand = java.util.Random()
val n = 1 + rand.nextInt(100)
var guess = 0
var numberOfTries = 0
println("I guessed a number from 1 до 100. What is it?\n")
do {
try {
guess = readLine()!!.toInt()
val x = Math.abs(n - guess)
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
}
} catch (e: NumberFormatException) {
println("Use digits, please!")
}
} while (guess != n)
println("Wow! You only used $numberOfTries tries!")

The Haskell way to do IO Loops (without explicit recursion)?

I want to read a list of strings seperated by newlines from STDIN, until a new line is witnessed and I want an action of the type IO [String]. Here is how I would do it with recursion:
myReadList :: IO String
myReadList = go []
where
go :: [String] -> IO [String]
go l = do {
inp <- getLine;
if (inp == "") then
return l;
else go (inp:l);
}
However, this method of using go obscures readability and is a pattern so common that one would ideally want to abstract this out.
So, this was my attempt:
whileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
whileM p [] = return []
whileM p (x:xs) = do
s <- x
if p s
then do
l <- whileM p xs
return (s:l)
else
return []
myReadList :: IO [String]
myReadList = whileM (/= "") (repeat getLine)
I am guessing there is some default implementation of this whileM or something similar already. However I cannot find it.
Could someone point out what is the most natural and elegant way to deal with this problem?
unfoldWhileM is same as your whileM except that it takes an action (not a list) as second argument.
myReadList = unfoldWhileM (/= "") getLine
Yes for abstracting out the explicit recursion as mentioned in the previous answer there is the Control.Monad.Loop library which is useful. For those who are interested here is a nice tutorial on Monad Loops.
However there is another way. Previously, struggling with this job and knowing that Haskell is by default Lazy i first tried;
(sequence . repeat $ getLine) >>= return . takeWhile (/="q")
I expected the above to collect entered lines into an IO [String] type. Nah... It runs indefinitely and IO actişons don't look lazy at all. At this point System IO Lazy might come handy too. It's a 2 function only simple library.
run :: T a -> IO a
interleave :: IO a -> T a
So run takes an Lazy IO action and turns it into an IO action and interleave does the opposite. Accordingly if we rephrase the above function as;
import qualified System.IO.Lazy as LIO
gls = LIO.run (sequence . repeat $ LIO.interleave getLine) >>= return . takeWhile (/="q")
Prelude> gls >>= return . sum . fmap (read :: String -> Int)
1
2
3
4
q
10
A solution using the effectful streams of the streaming package:
import Streaming
import qualified Streaming.Prelude as S
main :: IO ()
main = do
result <- S.toList_ . S.takeWhile (/="") . S.repeatM $ getLine
print result
A solution that shows prompts, keeping them separated from the reading actions:
main :: IO ()
main = do
result <- S.toList_
$ S.zipWith (\_ s -> s)
(S.repeatM $ putStrLn "Write something: ")
(S.takeWhile (/="") . S.repeatM $ getLine)
print result

2 dimension array processing in haskell

Sorry for my question which might seem trivial to some (I'm new). I have a file which contains a map looking like this :
---#--###----
-#---#----##-
------------#
In this file, – characters indicate that you are free to move in this direction. The # character indicates that you cannot move any further in this direction and you should go somewhere else. The # character indicates the location of the treasure. In this case, it is in the bottom right corner, but it could be anywhere in the map. So I have to go through these lines and see if I can reach the #. Here we are starting at the top left corner. So far I have managed to read the content of the file. And I'm wondering how to process this in Haskell. It will be easy in Java using a 2-dimensional array but how can I appproach this problem in Haskell?
For example, for the previous example, the path is:
+++#--###----
-#+--#----##-
--++++++++++#
The + symbol represents the path to the # symbol.
This the algorithm I have to implement it in Java:
Dfs(i,j) {
if (arr[i][j+1] == "-" && i >=0 && i<=row.size && j>=0 && j<=column.size) {
Dfs(i,j+1)
} else if(arr[i][j+1] == "#") {
}
if (arr[i][j-1] == "-" && i >=0 && i<=row.size && j>=0 && j<=column.size) {
Dfs(i,j-1)
} else if(arr[i][j-1] == "#") {
}
if (arr[i+1][j] == "-" && i >=0 && i<=row.size && j>=0 && j<=column.size) {
Dfs(i+1,j)
} else if(arr[i+1][j] == "#") {
}
}
Thank you
There are many ways of making 2D arrays in Haskell, here is a somewhat laborious example of reading the chars into a Data.Array array, and then moving things about with the so-called state monad:
import Data.Array
import Control.Monad.State.Strict
main = do str <- getContents -- accepts string from stdin
let array = mkThingArray str -- we parse the string
limits = snd (bounds array) -- we remember (height,width)
initialState = ((0::Int,-1::Int),limits,array)
((position,(h,w),a)) <- execStateT findpath initialState
let chars = elems $ fmap toChar a
putStrLn ""
putStrLn $ splitText (w+1) chars
parseArray str = listArray ((0,0),(height-1, width-1)) total where
rawlines = lines str
ls = filter (not . null) rawlines
lens = map length ls
height = length ls
width = minimum lens
proper = map (take width) ls
total = concat proper
data Thing = Open | Closed | Home | Taken deriving (Show, Eq, Ord)
toThing c = case c of '-' -> Open; '#' -> Closed; '#' -> Home;
'+' -> Taken; _ -> error "No such Thing"
toChar c = case c of Open -> '-'; Closed -> '#';
Home -> '#'; Taken -> '+'
mkThingArray str = fmap toThing (parseArray str)
And continuing with an absurdly primitive 'logic' of state change:
-- we begin with moveright, which may then pass on to movedown
-- and so on perhaps in a more sophisticated case
findpath = moveright
where
moveright = do ((n,m), (bound1,bound2), arr) <- get
if m < bound2
then case arr ! (n,m+1) of
Open -> do liftIO (putStrLn "moved right")
put ((n,m+1), (bound1,bound2), arr // [((n,m+1),Taken)])
moveright
Closed -> movedown
Home -> return ()
Taken -> movedown
else movedown
movedown = do ((n,m), (bound1,bound2), arr) <- get
if n < bound1
then case arr ! (n+1,m) of
Open -> do liftIO (putStrLn "moved down")
put ((n+1,m), (bound1,bound2), arr // [((n+1,m),Taken)])
moveright
Closed -> moveright
Home -> return ()
Taken -> moveright
else moveright
splitText n str = unlines $ split n [] str
where split n xss [] = xss
split n xss str = let (a,b) = splitAt n str
in if not (null a)
then split n (xss ++ [a]) b
else xss
which, in this happy case, gives output like this
{-
$ pbpaste | ./arrayparse
moved right
moved right
moved right
moved down
moved right
moved right
moved down
moved right
moved right
moved right
moved right
moved right
moved right
moved right
+++#--###----
-#+++#----##-
----++++++++#
-}
The logic will have to be more sophisticated, with moveleft and moveup, etc., etc. but this is supposed to give the idea, or an idea.
Edit: Here is a version that doesn't use an intermediate type and doesn't throw any IO into the state machine. It should be more usable in ghci, so you can tear it apart more easily:
import Data.Array
import Control.Monad.Trans.State.Strict
main = do str <- readFile "input.txt"
((pos,(h,w),endarray)) <- execStateT findpath
(mkInitialState str)
putStrLn $ prettyArray endarray
-- the following are just synonyms, nothing is happening:
type Pos = (Int, Int) -- Our positions are in 2 dimensions
type Arr = Array Pos Char -- Characters occupy these positions
type ArrState = (Pos, Pos, Arr) -- We will be tracking not just
-- an array of Chars but a
-- current position and the total size
parseArray :: String -> Arr
parseArray str = listArray ((1,1),(height, width)) (concat cropped) where
ls = filter (not . null) (lines str)
width = minimum (map length ls)
height = length ls
cropped = map (take width) ls -- the map is cropped to shortest line
prettyArray :: Arr -> String
prettyArray arr = split [] (elems arr)
where (ab,(h,w)) = bounds arr
split xss [] = unlines xss
split xss str = let (a,b) = splitAt w str
in if null a then unlines xss else split (xss ++ [a]) b
mkInitialState :: String -> ArrState
mkInitialState str = ((1::Int,0::Int), limits, array)
where array = parseArray str -- we parse the string
limits = snd (bounds array) -- we remember (height,width)
-- since we don't resize, tracking this could be avoided
makeStep :: Arr -> Pos -> Arr
makeStep arr (n, m) = arr // [((n,m),'+')] -- this is crude
moveRight, moveDown, findpath :: Monad m => StateT ArrState m ()
moveRight = do ((n,m),bounds,arr) <- get
put ((n,m+1), bounds, makeStep arr (n,m+1))
moveDown = do ((n,m),bounds,arr) <- get
put ((n+1,m), bounds, makeStep arr (n+1,m))
findpath = tryRight
where -- good luck for most paths ...
tryRight = do ((n,m), (_,bound2), arr) <- get
if m < bound2
then case arr ! (n,m+1) of
'#' -> return ()
'-' -> do moveRight
tryRight
_ -> tryDown
else tryDown
tryDown = do ((n,m), (bound1,_), arr) <- get
if n < bound1
then case arr ! (n+1,m) of
'#' -> return ()
'-' -> do moveDown
tryRight
_ -> tryRight
else tryRight
runInput :: String -> String
runInput str = prettyArray endarray
where ((position,(h,w),endarray)) = execState findpath (mkInitialState str)
-- If I wanted to include IO things in the state machine,
-- I would have to use execStateT not execState, which presupposes purity
test :: String -> IO ()
test str = putStrLn (runInput str)
t1 = unlines ["---#--###----"
, ""
, "-#---#----##-"
, ""
, "------------#"
] :: String
--
t2 = unlines ["---#--###----"
,""
,"---#-#----##-"
,""
,"------------#"
] :: String
This very much depends on the way you want to use your 2D array.
If you only care about sequential use, a simple list of lists (basically [[Char]]) may be fine.
If you care about efficient getting to particular random coordinates, I can imagine that an IntList IntList Char could work for you; it's almost like list of lists, but individual cells can be much more efficiently updated, and it gives cheap random access for pathfinding.
Possibly a zipper-like structure would suit you best. I can't (so far) imagine a nice structure of this type that gives you both cheap (O(1) per neighbor cell) navigation for pathfinding and cheap updates.
Also, you could use a mutable map via Monad.Control.State e.g. by keeping a Data.Array in it, but you will have to lift all your logic into this monad (which would complicate passing copies of the map around, when you need it).

Write a word at the end of a specific line in a text file using erlang

How can I write a word at the end of a specified line in a file in Erlang, let's say
Line 1: "He is john"
write_word("poem.txt",1," doe.").
Line 1: "He is john doe."
This is all I can manage to do:
write_word(Filename, LineNumber, Word) ->
{ok, Data} = file:open(FileName, [read, write]),
% write the word at end of line with the specified line number
Below provided is kind of a pseudo code(untested one). Also you will have to write the contents to a new file
openFile(FileName, Mode, DesiredLine) ->
{ok, FD} = file:open(FileName, Mode),
for_each_line(FD, 0, DesiredLine).
for_each_line(FD, LineNo, DesiredLine) ->
case io:get_line(FD, "") of
eof ->
file:close(FD);
Line ->
case LineNo =:= DesiredLine of
false ->
%% Write into a new file
NewLine = Line,
for_each_line(FD, LineNo + 1);
true ->
%% do your stuff
NewLine = Line ++ "Word",
%% write into your file
end
end.

Resources