REVISED: Sunday, September 14, 2025
1. OCAML READING AND WRITING TO A FILE
(*
This script performs several file and number operations:
1. It prompts the user to enter integers, writing them to a file named "integers.txt".
It handles file creation and appending automatically.
2. It reads the "integers.txt" file.
3. It identifies any prime numbers from the list of integers. A prime number is a natural number, a counting number, greater than 1 with no positive divisors or factors other than 1 and itself.
4. It writes the identified prime numbers to a file named "primes.txt".
5. Finally, it displays the full content of both "integers.txt" and "primes.txt".
*)
(*
==================================================================
PART 1: WRITE INTEGERS TO A FILE
==================================================================
This section handles capturing user input and writing it to 'integers.txt'.
*)
(*
The 'write_integers' function opens 'integers.txt' and recursively prompts
the user for input until they type 'exit'.
*)
let write_integers () =
(*
'Out_channel.open_gen' is used to open a file for writing.
- '[Open_append; Open_creat]' is a list of flags:
- 'Open_append': If the file exists, new content will be added to the end.
- 'Open_creat': If the file does not exist, it will be created.
- '0o666' sets the file permissions (read and write for owner, group, and others).
- '"integers.txt"' is the name of the file to open.
This combination elegantly handles both cases of the file existing or not.
*)
let oc = Out_channel.open_gen [Open_append; Open_creat] 0o666 "integers.txt" in
(*
A recursive function 'loop' is defined to continuously ask for user input.
This is a common pattern in functional programming for creating loops.
*)
let rec loop () =
print_string "Enter an integer (or 'exit' to finish): ";
(* 'flush stdout' ensures the prompt is immediately visible to the user. *)
flush stdout;
(* 'read_line ()' captures a line of text from the user's input. *)
let input = read_line () in
(* We check if the user's input is the word "exit". *)
if input <> "exit" then
(*
A 'try-with' block is used for error handling. It attempts to convert
the user's input string into an integer.
*)
try
(* 'int_of_string' attempts the conversion. *)
let num = int_of_string input in
(*
If successful, 'fprintf' writes the formatted string to the output channel 'oc'.
- '%d\n' is a format specifier: '%d' for an integer, '\n' for a new line.
This ensures each number is on its own line in the file.
*)
Printf.fprintf oc "%d\n" num;
(* The 'loop' function calls itself to ask for the next input. *)
loop ()
with
(*
If 'int_of_string' fails (e.g., input is "abc"), it raises a 'Failure' exception.
The 'with' block catches this exception.
*)
| Failure _ ->
(* An error message is printed to the console. *)
print_endline "Invalid input. Please enter an integer.";
(* The loop continues, asking for input again. *)
loop ()
in
(* The initial call to the recursive loop function to start the process. *)
loop ();
(*
It's crucial to close the output channel to ensure all data is written to the
file and to free up system resources.
*)
Out_channel.close oc;
print_endline "\nIntegers have been saved to integers.txt.\n"
;;
(*
==================================================================
PART 2: READ INTEGERS, FIND PRIMES, AND WRITE TO A NEW FILE
==================================================================
This section reads the created 'integers.txt' file, processes the numbers
to find primes, and writes them to 'primes.txt'.
*)
(*
The 'is_prime' function checks if a given integer is a prime number.
- 'n': The integer to check.
- Returns 'true' if prime, 'false' otherwise.
*)
let is_prime n =
(* Prime numbers must be greater than 1. *)
if n <= 1 then false
else
(*
A recursive helper function 'is_not_divisible_from' checks for factors.
- 'd': The current divisor to test.
*)
let rec is_not_divisible_from d =
(*
The base case for the recursion. If the divisor squared is greater than 'n',
it means we have checked all possible factors up to the square root of 'n'.
If no factors were found, the number is prime.
*)
if d * d > n then true
(*
'n mod d = 0' checks if 'd' is a factor of 'n' (i.e., division has no remainder).
If it is, 'n' is not prime.
*)
else if n mod d = 0 then false
(*
If 'd' is not a factor, we recursively call the function with the next
divisor, 'd + 1'.
*)
else is_not_divisible_from (d + 1)
in
(* The check for factors starts from 2. *)
is_not_divisible_from 2
;;
(*
This function reads integers from the input file, identifies the primes,
and writes them to the output file.
*)
let process_primes () =
(*
A 'try-with' block to handle the case where 'integers.txt' might not exist,
although our script always creates it. This is good practice for file I/O.
*)
try
(* Open the input file 'integers.txt' for reading. *)
let ic = In_channel.open_text "integers.txt" in
(*
Open the output file 'primes.txt' for writing, using the same append/create
strategy as in Part 1.
*)
let oc = Out_channel.open_gen [Open_append; Open_creat] 0o666 "primes.txt" in
(*
A recursive function to read each line from the input channel. It uses
pattern matching on the 'string option' returned by 'input_line'.
*)
let rec read_loop () =
match In_channel.input_line ic with
| Some line ->
(* A 'try-with' handles cases where a line is not a valid integer. *)
(try
(* The string from the file is converted back to an integer. *)
let num = int_of_string line in
(* The 'is_prime' function is called to test the number. *)
if is_prime num then
(* If it's a prime, it's written to the 'primes.txt' file. *)
Printf.fprintf oc "%d\n" num
with Failure _ -> ()); (* If conversion fails, ignore the line *)
(* The loop continues to the next line. *)
read_loop ()
(* 'None' indicates the end of the file. *)
| None -> () (* Stop the loop. *)
in
(* Start the reading loop. *)
read_loop ();
(* Close both the input and output file channels. *)
In_channel.close ic;
Out_channel.close oc;
print_endline "Prime numbers have been processed and saved to primes.txt.\n"
with
(* If 'integers.txt' cannot be opened, this error is caught. *)
| Sys_error msg -> Printf.eprintf "Error processing files: %s\n" msg
;;
(*
==================================================================
PART 3: DISPLAY THE CONTENTS OF BOTH FILES
==================================================================
This section reads and prints the final contents of both files to the console.
*)
(*
A reusable helper function to read and print the entire content of a given file.
- 'filename': The name of the file to read.
*)
let print_file_contents filename =
(* A descriptive header is printed. *)
print_endline ("---- Contents of " ^ filename ^ " ----");
try
(* Open the specified file for reading. *)
let ic = In_channel.open_text filename in
(*
A recursive function to read and print each line until the end of the
file is reached.
*)
let rec print_lines () =
match In_channel.input_line ic with
| Some line ->
print_endline line;
print_lines ()
| None -> () (* End of file, stop recursion. *)
in
print_lines ();
(* When the recursion is done, close the file. *)
In_channel.close ic
with Sys_error _ ->
(* If the file doesn't exist, print a message indicating that. *)
print_endline "(File is empty or does not exist)";
(* Print a blank line for better formatting. *)
print_endline ""
;;
(*
==================================================================
MAIN EXECUTION BLOCK
==================================================================
This is the main part of the script that calls the functions in order.
*)
(*
The 'let () =' syntax is used for running side-effecting code at the top level.
It defines a unit value, effectively just running the code on the right.
*)
let () =
(* 1. Call the function to get user input and write to 'integers.txt'. *)
s write_integer();
(* 2. Call the function to read 'integers.txt' and write primes to 'primes.txt'. *)
process_primes ();
(* 3. Print the complete contents of the 'integers.txt' file. *)
print_file_contents "integers.txt";
(* 4. Print the complete contents of the 'primes.txt' file. *)
print_file_contents "primes.txt"
;;
2. CONCLUSION
OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
PS C:\AI2025> & C:/Users/User/OneDrive/Documents/DOCUMENTS/AI2025/.venv/Scripts/Activate.ps1
(.venv) PS C:\AI2025> ocaml C:\AI2025\number_operations.ml
Enter an integer (or 'exit' to finish): 1
Enter an integer (or 'exit' to finish): 2
Enter an integer (or 'exit' to finish): 3
Enter an integer (or 'exit' to finish): 4
Enter an integer (or 'exit' to finish): 5
Enter an integer (or 'exit' to finish):
Invalid input. Please enter an integer.
Enter an integer (or 'exit' to finish): 6
Enter an integer (or 'exit' to finish): 7
Enter an integer (or 'exit' to finish): 8
Enter an integer (or 'exit' to finish): 9
Enter an integer (or 'exit' to finish): exit
Integers have been saved to integers.txt.
Prime numbers have been processed and saved to primes.txt.
---- Contents of integers.txt ----
1
2
3
4
5
6
7
8
9
---- Contents of primes.txt ----
2
3
5
7
(.venv) PS C:\AI2025>
3. REFERENCES
Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.
Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.
Goerzen, J. & O'Sullivan, B. & Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.
Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.
Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.
Pierce, Benjamin C., et al., editors. Logical Foundations. Vol. 1 of Software Foundations. Electronic Textbook, 2024. Version 6.7, http://softwarefoundations.cis.upenn.edu.
Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.