Printing randomly from a list OCAML - ocaml

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.

Related

How to process elements of an OCAML list?

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.

Read a file line per line and store every line read in a single list

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.

SML - Iterate through String

I'm trying to find if sentences read from a file has some pattern.
So far, I've written the code that reads all the sentences from file line by line, and puts those sentences to an array.
val infile = "c:/input.txt" ;
fun readlist (infile : string) =
let val ins = TextIO.openIn infile
fun loop ins = case TextIO.inputLine ins of
SOME line => line :: loop ins
| NONE => []
in loop ins before TextIO.closeIn ins
end;
val pureGraph = readlist(infile);
Try to write a function that evaluates to true if the letter a is in a string. Use explode to get a list of Chars. Recurse or fold over that list until you find a or reach the end. When you have that function, generalize it to any character. This will probably lead you to an O(n^2) runtime complexity.
Another approach is to sort the character list, remove duplicates, zip it with the correct list of characters and compare each tuple with recursion/fold. This should run in O(n log n) time because of the sort.
A third approach is to fold over the character list with an array or a hash map. In the array or map you add in what the current character is. At the end you see if all characters were found. This approach should run in O(n) time if your hashmap is constant-time.
Divide and conquer your problem:
Write a function isPanagram : string -> bool that determines this for a single line.
One strategy could be: Start with the set of all letters. Loop through the string, and for each character in the string, remove it from the set until the end of the string, or the set is empty. If the set is empty, it is a panagram. This requires that you represent sets in some way, e.g. with a list or a binary search tree.
Consider looping through the string by index, rather than exploding it:
val allLetters = ...
fun remove x ... = ...
fun isEmpty ... = ...
fun isPanagram s =
let val len = size s
fun loop i missingLetters =
isEmpty missingLetters orelse
i < len andalso loop (i+1) (remove (String.sub (s, i)) missingLetters)
in loop 0 allLetters end
Write a function readLines : string -> string list that reads the content of a file and separates the lines into elements of a list:
fun isLinebreak c = c = #"\r" orelse c = #"\n"
fun readLines filename =
let val ins = TextIO.openIn filename
val data = TextIO.inputAll ins
val _ = TextIO.closeIn ins
in String.tokens isLinebreak data end
(Yes, reading the file one line at a time will save memory.)
The SML/NJ library has a number of data structures which can be used for things like sets and hash tables. They are not exactly well-documented, but this explains a bit how to use them. Using their set library, you can write something like this:
structure CharSet = RedBlackSetFn(struct
type ord_key = char
val compare = Char.compare
end)
val alphabet = CharSet.fromList (explode "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
fun isPanagram s =
let val chars = CharSet.fromList (map Char.toUpper (explode s))
val letters = CharSet.intersection (chars,alphabet)
in CharSet.numItems letters = 26
end;
used like this:
- isPanagram "We promptly judged antique ivory buckles for the next prize.";
val it = true : bool
- isPanagram "We promptly judged antique plastic buckles for the next prize.";
val it = false : bool

prompt user to build a string 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.

Filtering a list of arrays f#

sorry if the questions to basic, but i havent been able to do this for some time. I have created a lists of lists in which the second array contains a parameter that can be either an f or a p. I need to create two new lists of arrays, one containing the items that have the f parameter and the other one containing the p parameter.
edit: trying to explain myself:
I have a list containing a series of facebook publications, and each one of this publications has information, such as what type of publication it is.. they can be either a p (text) or f (picture). What i need to do is to create two separate lists of this publications by the type publication they are.
example of data: [[|"publication0ID", "Poster0ID","TypeofPublication0"|];[|"publication1ID", "Poster1ID","TypeofPublication1"|]]
let data = [[|"publication0ID"; "Poster0ID"; "f"|];[|"publication1ID"; "Poster1ID"; "p"|]]
let texts, pictures =
data
|> List.partition (List.ofArray >> function
| _ :: _ :: "f" :: _ -> true
| _ :: _ :: "p" :: _ -> false
| _ -> failwith "neither f nor p"
)
This will split the lists according to the third "parameter", which you called "TypeOfPublication".
I changed your sample code, because your sub-arrays sub-lists contain only one tuple and judging by your "..." I tought that might be wrong.
To explain:
List.partition splits a list according to a function that is called for every element in the list. When the function returns true, the element will be put into the first list of the result tuple, and into the second list when false.
Since your elements are arrays also lists, it will be checked if the third element in the array list is either "f", which will cause the array list to be put in the texts result, and "p", which will be put into pictures.
If the third element is neither "f" nor "p", an exception will be thrown.
Update for the comment:
If your sub-arrays are always exactly three elements long, you can use this version:
let texts, pictures =
data
|> List.partition (function
| [| _; _; "f" |] -> true
| [| _; _; "p" |] -> false
| _ -> failwith "neither f nor p or wrong array length"
)
Or, you can use the first version and just put List.ofArray >> in between the function keyword and the opening paren so that it reads: List.partition (List.ofArray >> function (I updated the code above as well).
Assuming that your main list is of type (int, string) list list, then if you
let f = 1
let p = 1
you should be able to filter your main_list by using
let f_items = seq {
let! sub_list = main_list
let! (selector, item) = sub_list
if selector == f then
yield item
}
and likewise, to get the "p" items, you would use selector == p.
I had to bring out my F# book to be able to write this code, I haven't used F# for so long! I don't have F# on this computer, so I don't know if the above code actually works.