getLines = liftM lines . readFile
main = do
argv <- getArgs
name <- getProgName
if not (null argv)
then do
let file = head argv
list <- getLines file
let olist = mergesort (<=) list
let splitter = map (split ",") olist
loop splitter
else hPutStr stderr $ "usage: " ++ name ++ " filename"
loop a = do
line <- getLine
case line of
"help" -> putStrLn "print - prints list in alphabetical order\n\
\quit - exits program"
"print" -> do putStrLn "[print]"
mapM_ putStrLn a
putStr "\n"
"quit" -> do putStrLn "[quit]"
exitSuccess
_ -> putStrLn "invalid command"
loop a
I'm trying to split a list of lines that each have a comma, except I get an error at, loop splitter. I think its because mapM_ putStrLn within my loop function can't understand the new list. Before i used the splitter function my program would print out the entire list taken from a text file with no problems. How can i print out a new list of words that were split at the comma?
Related
How do I print each line in a text file only once but in a random order?
I have a text file that containts six individual lines and I am trying to print them to the screen randomly
Here is the code I have so far
open Scanf
open Printf
let id x = x
let const x = fun _ -> x
let read_line file = fscanf file "%s#\n" id
let is_eof file = try fscanf file "%0c" (const false) with End_of_file -> true
let _ =
let file = open_in "text.txt" in
while not (is_eof file) do
let s = read_line file in
printf "%s\n" s
done;
close_in file
I could append elements "s" into a list. Printing elements in a list can be as simple as following however, I am not sure how to print elements in the list randomly.
let rec print_list = function
[] -> ()
| e::l -> print_int e ; print_string " " ; print_list l
Sort your list with random comparator. For example by the following function.
let mix =
let random _ _ =
if Random.bool() then 1 else -1 in
List.sort random
Edit 1 (15.11.20)
List.sort implements Merge Sort algorithm. Merge Sort has stable O(n log n). Also steps count of this algorithm is not dependent on results of items comparison. It means our random function that is nondeterministic doesn't effect the time of List.sort work. (The following image is from wikipedia)
If our input data is list and we can't use mutable data structures - I think it is impossible to implement solution with better Big O than O(n log n) because of immutable list and necessity to have random access to items.
let's define a function that retrieve one element identified by its position in a list, and return a tuple (this_element, the_list_wo_this_element).
Ex : pick [0;2;4;6;8] 3
returns (6, [0;2;4;8)).
Then, by iterating on the resulted list (the rhs of the tuple above), you pick a random element from that list , until that list is empty.
I'm a student and I've been given a exercice i've been struggling with for about a month or so.
I'm trying to write a function in Ocaml. This function must read a text file which has a word per line, and it must store all the words in a list.
But the problem is that this program must be a recursive one (which means no loops, no "while").
All I've been able to do so far is to create a function which reads the text file (pretty much like the BASH command "cat")
let dico filename =
let f = open_in filename in
let rec dico_rec () =
try
print_string (input_line f);
print_newline ();
dico_rec();
with End_of_file -> close_in f
in dico_rec() ;;
I just don't know how to do it. Ocaml is hardly my favourite language.
Here's an alternate definition of build_list that is tail recursive. You can use it instead of #MitchellGouzenko's definition if your inputs can have many lines.
let rec build_list l =
match input_line ic with
| line -> build_list (line :: l)
| exception End_of_file -> close_in ic; List.rev l
open Printf
let file = "example.dat"
let () =
let ic = open_in file in
let rec build_list infile =
try
let line = input_line infile in
line :: build_list(infile)
with End_of_file ->
close_in infile;
[] in
let rec print_list = function
[] -> ()
| e::l -> print_string e ; print_string " " ; print_list l in
print_list(build_list(ic))
Edit: The algorithm I previously proposed was unnecessarily complicated. Try to understand this one instead.
To understand this recursion, we assume that build_list works correctly. That is, assume build_list correctly takes an open file as an argument and returns a list of lines in the file.
Now, let's look at the function's body. It reads a line from the file and calls build_list again. If there are N lines in the file, calling build_list again should return a list of the remaining N-1 lines in the file (since we just read the first line ourselves). We append the line we just read to the list returned from build_list, and return the resulting list, which has all N lines.
The recursion continues until it hits the base case. The base case is when there's an End_of_file. In this case we return an empty list.
I would like to build a string list by prompting the user for input. My end goal is to be able to parse a string list against a simple hash table using a simple routine.
`let list_find tbl ls =
List.iter (fun x ->
let mbr = if Hashtbl.mem tbl x then "aok" else "not found"
in
Printf.printf "%s %s\n" x mbr) ls ;;`
Building a string list is accomplished with the cons operator ::, but somehow I am not able to get the prompt to generate a string list. A simpe list function returns anything that is put into it as a list:
`let build_strlist x =
let rec aux x = match x with
| [] -> []
| hd :: tl -> hd :: aux tl
in
aux x ;;`
Thus far, I have been able to set the prompt, but building the string list did not go so well. I am inclined to think I should be using Buffer or Scanning.in_channel. This is what I have thus far:
`#load "unix.cma" ;;
let prompt () = Unix.isatty Unix.stdin && Unix.isatty Unix.stdout ;;
let build_strlist () =
let rec loop () =
let eof = ref false in
try
while not !eof do
if prompt () then print_endline "enter input ";
let line = read_line () in
if line = "-1" then eof := true
else
let rec build x = match x with
| [] -> []
| hd :: tl -> hd :: build tl
in
Printf.printf "you've entered %s\n" (List.iter (build line));
done
with End_of_file -> ()
in
loop () ;;`
I am getting an error the keyword "line" has the type string, but an expression was expected of type 'a list. Should I be building the string list using Buffer.create buf and then Buffer.add_string buf prepending [ followed by quotes " another " and a semicolon? This seems to be an overkill. Maybe I should just return a string list and ignore any attempts to "peek at what we have"? Printing will be done after checking the hash table.
I would like to have a prompt routine so that I can use ocaml for scripting and user interaction. I found some ideas on-line which allowed me to write the skeleton above.
I would probably break down the problem in several steps:
get the list of strings
process it (in your example, simply print it back)
1st step can be achieved with a recursive function as follow:
let build_strlist' () =
let rec loop l =
if prompt () then (
print_string "enter input: ";
match read_line () with
"-1" -> l
| s -> loop (s::l)
) else l
in loop [];;
See how that function loops on itself and build up the list l as it goes. As you mentioned in your comment, I dropped the imperative part of your code to keep the functional recursion only. You could have achieved the same by keeping instead the imperative part and leaving out the recursion, but recursion feels more natural to me, and if written correctly, leads to mostly the same machine code.
Once you have the list, simply apply a List.iter to it with the ad hoc printing function as you did in your original function.
I would write (for debugging) a printer for a list of ((string * string) * 'a)
I tried with:
(* print a list of ((string * string) * 'a) *)
let rec print_list = function
[] -> Printf.printf "\n empty list \n %!"; ()
| ((s1,s2),a)::l -> Printf.printf "\n(s1,s2)= %s %s \n %!" s1 s2; print_list l;;
but it doesn't print anything.
It should print at least empty list, right?
In many languages (C and Ocaml) you'll better put the \n at end of printf format control string, or force the stdout to be flushed. Output to stdout is usually buffered (for performance reasons).
You should read the documentation of Ocaml before coding in Ocaml.
You may also want to call (from Pervasives) print_newline; read carefully documentation of Printf module; you could end your printf format string with %!
Here is an example (copied from your edited question) with Ocaml 4.01:
% ocaml
OCaml version 4.01.0
# (* print a list of ((string * string) * 'a) *)
let rec print_list = function
[] -> Printf.printf "\n empty list \n %!"; ()
| ((s1,s2),a)::l -> Printf.printf "\n(s1,s2)= %s %s \n %!" s1 s2;
print_list l;;
val print_list : ((string * string) * 'a) list -> unit = <fun>
# print_list [];;
empty list
- : unit = ()
#
As you can see, empty list gets printed. In the above code, % is a shell prompt, # is an ocaml toplevel prompt. Your function is copied verbatim. print_list [];; is input at toplevel.
I'm trying to write a program that allows the user to build up a list of strings by entering them in one at a time, and displays the list after every step.
Here is my code so far:
buildList :: [String] -> IO ()
buildList arr = do
putStr "Enter a line:"
str <- getLine
if str == "" then
return ()
else do
let newarr = arr : str
putStrLn ("List is now: " ++ newarr)
buildList newarr
listBuilder :: IO ()
listBuilder = do
buildList []
listBuilder is starting the list by passing in the empty list, and I'm trying to use recursion so that the code keeps running until the user enters the empty string.
Its not working, any ideas welcome
Here is a desired input:
Enter a line: hello
List is now ["hello"]
Enter a line: world
List is now ["hello","world"]
Enter a line:
Error:
Couldn't match type `Char' with `[String]'
Expected type: [[String]]
Actual type: String
In the second argument of `(:)', namely `str'
In the expression: arr : str
In an equation for `newarr': newarr = arr : str
EDIT:
This fixed it, thanks to the clues and use of show
buildList :: [String] -> IO ()
buildList arr = do
putStr "Enter a line:"
str <- getLine
if str == "" then
return ()
else do
let newarr = arr++[str]
putStrLn ("List is now: " ++ show newarr)
buildList newarr
listBuilder :: IO ()
listBuilder = do
buildList []
You can get this working by
(a) putting the new string at the end of the list with arr++[str] instead of arr:str since : can only be used like singleThing:list,
(b) splitting the run-round into a separate function, and
(c) passing the result on with return so you can use it elsewhere in your program
buildList arr = do
putStrLn "Enter a line:"
str <- getLine
if str == "" then
return arr
else do
tell (arr++[str])
tell arr = do
putStrLn ("List is now: " ++ show arr) -- show arr to make it a String
buildList arr
giving
Enter a line:
Hello
List is now: ["Hello"]
Enter a line:
world
List is now: ["Hello","world"]
Enter a line:
done
You can solve this problem more declaratively using the pipes and foldl libraries:
import Control.Foldl (purely, list)
import Pipes
import qualified Pipes.Prelude as P
main = runEffect $ P.stdinLn >-> purely P.scan list >-> P.print
You can read this as a pipeline:
P.stdinLn is a source of lines input by the user
P.scan behaves like Data.List.scanl, except for pipelines instead of lists. purely P.scan list says to continuously output the values seen so far.
P.print prints these output lists to the console
Here's an example of this pipeline in action:
$ ./example
[]
Test<Enter>
["Test"]
ABC<Enter>
["Test","ABC"]
42<Enter>
["Test","ABC","42"]
<Ctrl-D>
$
You can also easily switch out other ways to fold the lines just by changing the argument to purely scan. For example, if you switch out list with Control.Foldl.vector then it will output vectors of lines instead of lists.
To learn more, you can read the documentation for the pipes and foldl libraries.
The problem is that the : data constructor can only be used to append an element to the beginning of the list. When you write let arr=arr:str, you are using it to put an element at the end of the list. Instead, you can either construct your list backwards like this let arr=str:arr or use the ++ operator to append it to the end of the list like this let arr=arr++[str].