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.
Related
I'm trying to create a List reading a text file, for example I have a text file like this "1 5 12 9 2 6" and I want to create a list like this [1,5,12,9,2,6] using SML
You can divide this task into several sub-problems:
Reading a file into a string can be done with
type filepath = string
(* filepath -> string *)
fun readFile filePath =
let val fd = TextIO.openIn filePath
val s = TextIO.inputAll fd
val _ = TextIO.closeIn fd
in s end
See the TextIO library.
Converting a string into a list of strings separated by whitespace can be done with
(* string -> string list *)
fun split s =
String.tokens Char.isSpace s
See the String.tokens function.
Converting a list of strings into a list of integers can be done with
(* 'a option list -> 'a list option *)
fun sequence (SOME x :: rest) = Option.map (fn xs => x :: xs) (sequence rest)
| sequence (NONE :: _) = NONE
| sequence [] = SOME []
fun convert ss = sequence (List.map Int.fromString ss)
Since any one string-to-integer conversion with Int.fromString may fail and produce a NONE, List.map Int.fromString will produce an "int option list" rather than an "int list". This list of "int option" may be converted to an optional "int list", i.e., remove the SOME of all the "int option", but if there's a single NONE, the entire result is discarded and becomes NONE. This gives the final type "int list option" (either NONE or SOME [1,2,...]).
See the Option.map function which was useful for this kind of recursion.
Combining these,
(* filepath -> int list *)
fun readIntegers filePath =
convert (split (readFile filePath))
This approach does yield some potentially unwanted behavior:
Filesystem errors will make readIntegers throw an Io exception
The string ~5 inside the file will be interpreted as negative five
The string -5 will produce a failure (NONE)
The string 123a will produce the number 123 (Int.toString is a bit too forgiving)
You may want to address those.
I'm learning ocaml so it's maybe trivial.
When I try to build executable of this code:
open Core.Std
let build_counts () =
In_channel.fold_lines stdin ~init:[] ~f:(fun counts line ->
let count =
match List.Assoc.find counts line with
| None -> 0
| Some x -> x
in
List.Assoc.add counts line (count + 1)
)
let () =
build_counts ()
|> List.sort ~cmp:(fun (_,x) (_,y) -> Int.descending x y)
|> (fun l -> List.take l 10)
|> List.iter ~f:(fun (line,count) -> printf "%3d: %s\n" count line)
I get this error:
Error: This pattern matches values of type 'a option
but a pattern was expected which matches values of type
equal:(Stdio__.Import.string -> Stdio__.Import.string -> bool) ->
'b option
Where is the problem?
Link: https://realworldocaml.org/v1/en/html/files-modules-and-programs.html
Below is the type signature for List.Assoc.find:
('a, 'b) Base__List.Assoc.t -> equal:('a -> 'a -> bool) -> 'a -> 'b option
The first argument is the associative list (counts in the example). The last argument (of type 'a) is the key you're looking for (this is line in your example). There is another argument, however, of type 'a -> 'a -> bool which labeled equal. It's pretty straight-forward that it is a comparison function used by List.Assoc.find to see whether two keys are equals.
In the case where 'a is string, a simple (=) is enough. You can fix your code by replacing your match line with the following:
match List.Assoc.find counts ~equal:(=) line with
The List.Assoc.add function follows the same pattern. You should replace the last line of the build_counts function with the following:
List.Assoc.add counts ~equal:(=) line (count + 1)
As a side-note, Real World OCaml is getting quite old (this is why some examples are outdated), and the authors are working on a second edition.
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 can't wrap my head around where should I put parenthesis to get it working:
let read_lines filename =
let channel = open_in filename in
Std.input_list channel;;
let print_lines filename =
List.map print_string ((^) "\n") (read_lines filename);;
^ This is the closes I've got so far. If my terminology is vague: ((^) "\n") is what I call partial function (well, because it doesn't handle all of its arguments). print_string I call total function because... well, it handles all of its arguments.
Obviously, what I would like to happen is that:
List.map applies first ((^) "\n") to the element of the list.
List.map applies print_string to the result of #1.
How? :)
Maybe you want something like that?
# let ($) f g = fun x -> f(g x);;
val ( $ ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
# let f = print_string $ (fun s -> s^"\n");;
val f : string -> unit = <fun>
# List.iter f ["a";"b";"c";"d"];;
a
b
c
d
- : unit = ()
# let g = string_of_int $ ((+)1) $ int_of_string;;
val g : string -> string = <fun>
# g "1";;
- : string = "2"
Your code didn't work because missing parenthesis:
List.map print_string ((^) "\n") xs
is parsed as
(List.map print_string ((^) "\n")) xs
when you expected
List.map (print_string ((^) "\n")) xs
A few things: List.map is probably not what you want, since it will produce a list (of unit values) rather than just iterating. ((^) "\n") is probably also not what you want, as it prepends a newline, the "\n" being the first argument. (This is not a section as in Haskell, but a straightforward partial application.)
Here's a reasonable solution that is close to what (I think) you want:
let print_lines filename =
List.iter (fun str -> print_string (str ^ "\n")) (read_lines filename)
But I would rather write
let print_lines filename =
List.iter (Printf.printf "%s\n") (read_lines filename)
Which is both clearer and more efficient.
I want to do something as simple as this:
Print a list.
let a = [1;2;3;4;5]
How can I print this list to Standard Output?
You should become familiar with the List.iter and List.map functions. They are essential for programming in OCaml. If you also get comfortable with the Printf module, you can then write:
open Printf
let a = [1;2;3;4;5]
let () = List.iter (printf "%d ") a
I open Printf in most of my code because I use the functions in it so often. Without that you would have to write Printf.printf in the last line. Also, if you're working in the toploop, don't forget to end the above statements with double semi-colons.
You can do this with a simple recursion :
let rec print_list = function
[] -> ()
| e::l -> print_int e ; print_string " " ; print_list l
The head of the list is printed, then you do a recursive call on the tail of the list.
print_string (String.concat " " (List.map string_of_int list))
If the question is about finding the quickiest way to implement this, for example when debugging, then we could say that:
extended standard libraries (e.g. batteries) typically have some additional functions:
List.print
~first:"[" ~sep:";" ~last:"]" (fun c x -> Printf.fprintf c "%d" x) stdout a
this tiny syntax extension that I wrote some time ago allows you to write:
<:print<[$!i <- a${$d:i$}{;}]>>
automatic generation is not immediately available (because of the lack of run-time type information in OCaml data representation) but can be achieved using either code generation from the types, or run-time types.
I'm very late answering, but here's another way:
let print_list f lst =
let rec print_elements = function
| [] -> ()
| h::t -> f h; print_string ";"; print_elements t
in
print_string "[";
print_elements lst;
print_string "]";;
To print an int list, we could write:
print_list print_int [3;6;78;5;2;34;7];;
However if we were going to do this a lot, it would save time to specialize the function using partial application:
let print_int_list = print_list print_int;;
Which we can now use like so:
print_int_list [3;6;78;5;2;34;7];;
What if we wanted to do something pretty complex, like printing an int list list? With this function, it's easy:
(* Option 1 *)
print_list (print_list print_int) [[3;6;78];[];[5];[2;34;7]];;
(* Option 2 *)
let print_int_list_list = print_list (print_list print_int);;
print_int_list_list [[3;6;78];[];[5];[2;34;7]];;
(* Option 3 *)
let print_int_list_list = print_list print_int_list;;
print_int_list_list [[3;6;78];[];[5];[2;34;7]];;
Printing an (int * string) list (i.e. a list of pairs of ints and strings):
(* Option 1 *)
print_list (fun (a, b) -> print_string "("; print_int a; print_string ", "; print_string b; print_string ")") [(1, "one"); (2, "two"); (3, "three")];;
(* Option 2 *)
let print_pair f g (a, b) =
print_string "(";
f a;
print_string ", ";
g b;
print_string ")";;
print_list (print_pair print_int print_string) [(1, "one"); (2, "two"); (3, "three")];;
(* Option 3 *)
let print_pair f g (a, b) =
print_string "(";
f a;
print_string ", ";
g b;
print_string ")";;
let print_int_string_pair = print_pair print_int print_string;;
print_list print_int_string_pair [(1, "one"); (2, "two"); (3, "three")];;
(* Option 4 *)
let print_pair f g (a, b) =
print_string "(";
f a;
print_string ", ";
g b;
print_string ")";;
let print_int_string_pair = print_pair print_int print_string;;
let print_int_string_pair_list = print_list print_int_string_pair;;
print_int_string_pair_list [(1, "one"); (2, "two"); (3, "three")];;
I would do this in the following way:
let a = [1;2;3;4;5];;
List.iter print_int a;;
Actually, you can decouple printing a list and turning a list into a string. The main advantage for doing this is that you can use this method to show lists in logs, export them to CSVs...
I often use a listHelper module, with the following :
(** Generic method to print the elements of a list *)
let string_of_list input_list string_of_element sep =
let add a b = a^sep^(string_of_element b) in
match input_list with
| [] -> ""
| h::t -> List.fold_left add (string_of_element h) t
So, if I wanted to output a list of floats to a csv file, I could just use the following :
let float_list_to_csv_row input_list = string_of_list input_list string_of_float ","
Just a solution with %a :
open Printf
let print_l outx l =
List.map string_of_int l
|> String.concat ";"
|> fprintf outx "%s"
Test :
# printf "[%a]" print_l [1;2;3] ;;
[1;2;3]- : unit = ()
# printf "[%a]" print_l [];;
[]- : unit = ()
let print_list l =
let rec aux acc =
match acc with
| [] -> ()
| x :: tl ->
Printf.fprintf stdout "%i"; aux tl
in aux l
Or
let sprintf_list l =
let acc = ref "{" in
List.iteri (fun i x ->
acc := !acc ^
if i <> 0
then Printf.sprintf "; %i" x
else Printf.sprintf "%i" x
) l;
!acc ^ "}"
let print_list l =
let output = sprintf_list l in
Printf.fprintf stdout "%s\n" output