In the ocaml-containers documentation, it gives a great example, how to read in a file, and write the content to a different file here. However, I am trying to see what it would take to modify the text before it is passed from the file being read to file being written.
let read_filename = "example.ts"
let filename = "example2.ts"
let () =
let modify_file ~chunks =
let r = Str.regexp "text \\([A-Za-z]+\\)" in
Str.replace_first r "\\1" chunks in
CCIO.(
with_in read_filename
(fun ic ->
let chunks = read_chunks ic in
let new_chunks = modify_file chunks in
with_out ~flags:[Open_binary] ~mode:0o644 filename
(fun oc ->
write_gen oc new_chunks
)
)
)
The issue with this code, is that the compiler complains:
File "component.ml", line 13, characters 39-45:
Error: This expression has type string gen = unit -> string option
but an expression was expected of type string
I am trying to figure out what I am doing wrong, but to no avail. Any help would be more than appreciated. Also, suggestions as to the ideal enterprise software to use in OCaml to modify text in files, is more than appreciated. Thank you.
You have a typing problem
read_chunk ic return a string gen value
from here we learn that is a function that take unit value () and return a string.
let () =
CCIO.(
let modify_file ~chunks =
let r = Str.regexp "example \\([A-Za-z]+\\)" in
match chunks () with
None -> chunks (* is the same as (fun () -> None) *)
| Some chunks ->
let chunks = Str.replace_first r "\\1" chunks in (* compute once *)
(fun () -> Some chunks) in
with_in read_filename
(fun ic ->
let chunks = read_chunks ic in
let new_chunks = modify_file ~chunks in
with_out ~flags:[Open_binary] ~mode:0o644 filename
(fun oc ->
write_gen oc new_chunks
)
)
)
EDIT:
explain the error and the change
EDIT 2:
I have modifier modify_file so it return antoher string gen and corrected the syntax error: when you use labeled argument you need to add a tilde when calling it
What is the type of modify_file. I don't see it in the manual.
You should try to add unit value () this way
let new_chunks = modify_file chunks () in and new_chunks will be of type string option
You can pattern match as option type.
EDIT:
I have seen that third parameter has type string gen
You should modify the 16th line this way:
write_gen oc (fun () -> new_chunks)
Related
I am currently trying to use ocaml-re. Documentation is sparse. I was wondering how I would, for instance, do the equivalent:
Str.regexp "example \\([A-Za-z]+\\)" using Re.Perl? I think it would help me to naturally get the rest of the documentation on my own. Thank you!
Bonus points if you convert this code from Str to Re.Perl:
let read_filename = "example.ts"
let filename = "example2.ts"
let () =
CCIO.(
let modify_file ~chunks =
let r = Str.regexp "example \\([A-Za-z]+\\)" in
match chunks () with
None -> chunks (* is the same as (fun () -> None) *)
| Some chunks ->
let test_chunks = Str.replace_first r "\\1" chunks in (* compute once *)
(fun () -> Some test_chunks) in
with_in read_filename
(fun ic ->
let chunks = read_chunks ic in
let new_chunks = modify_file ~chunks in
with_out ~flags:[Open_binary] ~mode:0o644 filename
(fun oc ->
write_gen oc new_chunks
)
)
)
Don't use Re.Perl, Re's API is much simpler. You can constructor your re with:
let re =
let open Re in
alt [rg 'A' 'Z'; rg 'a' 'z'] (* [A-Za-z] *)
|> rep1a (* [A-Za-z]+ *)
|> group (* ([A-Za-z]+) *)
|> compile
I am quite new to OCaml, so I am not sure what the following error message means (specifically the /0 and the /-1):
Error: This expression has type (string, string) Hashtbl/0.t
but an expression was expected of type ('a, 'b) Hashtbl/-1.t
I am passing a Hashtbl.t into Hashtbl.find and this error shows up. I am unclear as to how the /0 and /-1 came in, and what they actually mean.
Here's a minimal working example to demonstrate my issue:
open Core_kernel.Std
let file_to_hashtbl filename =
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str))
let ht = file_to_hashtbl "test"
let t1_val = match Hashtbl.find ht "t1" with
| Some v -> v
| None -> assert false
let () = print_endline t1_val
Let's show you an example :
If I write
type t = A;;
let x = A;;
type t = B;;
let y = B;;
x = y;;
Error: This expression has type t/1561 but an expression was expected of type
t/1558
This is because in the interpreter you can declare multiple types with the same name and associate values to these types. But here, as you can see, x and y are not of the same type but both the types are named t so the interpreter tries to tell you the types are both named t but are not the same.
[Compilation]
If I wanted to compile this, I would have to declare
typea.ml
type t = A
let x = A
typeb.ml
type t = B
let y = B
main.ml
open Typea
open Typeb
x = y
If I compile this I will have
Error: This expression has type Typeb.t
but an expression was expected of type Typea.t
What lesson should you learn from this ? Stop interpreting, compile !
Now that I managed to compile your file, I got an error too but much more explicit :
Error: This expression has type (string, string) Hashtbl.t
but an expression was expected of type
('a, 'b) Core_kernel.Std.Hashtbl.t =
('a, 'b) Core_kernel.Core_hashtbl.t
[Explanation and correction]
Since I'm too nice, here is your file corrected :
let file_to_hashtbl filename =
(* open the namespace only where needed *)
let open Core_kernel.Std in
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str));;
let ht = file_to_hashtbl "test"
let t1_val =
try
Hashtbl.find ht "t1"
with Not_found -> assert false
let () = print_endline t1_val
Your error was that you opened Core_kernel.Std as a global namespace so when you wrote Hashtbl.find it looked first in Core_kernel.Std and not in the standard library.
What I did is open Core_kernel.Std in the function that needs it, not in the whole file (so it's a local namespace) (a good habit to take).
So, as you can see, the problem was that you had two definition of the type Hashtbl.t (one in Core_kernel.Std and one in the standard library) and OCaml ain't no fool, boy, he knows when you're wrong but he is hard to understand since he only speak for those who can hear. :-D
P.S. : You had an error in your Hashtbl.find because it doesn't return an option but the found value or raise a Not_found exception if no value was found. I corrected it too. ;-)
Apparently, it is just a matter of missing semi-columns, the foloowing code compiles :
open Core_kernel.Std;;
let file_to_hashtbl filename =
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str));;
let ht = file_to_hashtbl "test"
let t1_val = match Hashtbl.find ht "t1" with
| Some v -> v
| None -> assert false
let () = print_endline t1_val
But, I do not know how to interpret the error message neither.
I have a input.txt file with few lines of text. I am trying to store those lines in a list l. I think I am doing correct but list l is not getting updated. please help.
let l = []
let () =
let ic = open_in "input.txt"
in
try
while true do
let line = input_line ic
in
let rec append(a, b) = match a with
|[] -> [b]
|c::cs -> c::append(cs,b)
in
append(l, line)
(* print_endline line *)
done
with End_of_file ->
close_in ic;;
Apart from Warning 10, I am not getting any error.
let l = []
Variables in OCaml are immutable, so no matter what code you write after this line, l will always be equal to [].
It looks like you are caught in imperative programming - a good thing to start with OCaml!
Typical functional and recursive programming would read a file like this:
Read a line, then append "read a line" to it. At End_of_File you finish the list with [].
I'm trying to use the CIL library to parse C source code. I'm searching for a particular function using its name.
let cil_func = Caml.List.find (fun g ->
match g with
| GFun(f,_) when (equal f.svar.vname func) -> true
| _ -> false
) cil_file.globals in
let body g = match g with GFun(f,_) -> f.sbody in
dumpBlock defaultCilPrinter stdout 1 (body cil_func)
So I have a type GFun of fundec * location, and I'm trying to get the sbody attribute of fundec.
It seems redundant to do a second pattern match, not to mention, the compiler complains that it's not exhaustive. Is there a better way of doing this?
You can define your own function that returns just the fundec:
let rec find_fundec fname = function
| [] -> raise Not_found
| GFun (f, _) :: _ when equal (f.svar.vname fname) -> f (* ? *)
| _ :: t -> find_fundec fname t
Then your code looks more like this:
let cil_fundec = find_fundec func cil_file.globals in
dumpBlock defaultCilPrinter stdout 1 cil_fundec.sbody
For what it's worth, the line marked (* ? *) looks wrong to me. I don't see why f.svar.vname would be a function. I'm just copying your code there.
Update
Fixed an error (one I often make), sorry.
I have a function save that take standard input, which is used individually like this:
./try < input.txt (* save function is in try file *)
input.txt
2
3
10 29 23
22 14 9
and now i put the function into another file called path.ml which is a part of my interpreter. Now I have a problem in defining the type of Save function and this is because save function has type in_channel, but when i write
type term = Save of in_channel
ocamlc complain about the parameter in the command function.
How can i fix this error? This is the reason why in my last question posted on stackoverflow, I asked for the way to express a variable that accept any type. I understand the answers but actually it doesn't help much in make the code running.
This is my code:
(* Data types *)
open Printf
type term = Print_line_in_file of int*string
| Print of string
| Save of in_channel (* error here *)
;;
let input_line_opt ic =
try Some (input_line ic)
with End_of_file -> None
let nth_line n filename =
let ic = open_in filename in
let rec aux i =
match input_line_opt ic with
| Some line ->
if i = n then begin
close_in ic;
(line)
end else aux (succ i)
| None ->
close_in ic;
failwith "end of file reached"
in
aux 1
(* get all lines *)
let k = ref 1
let first = ref ""
let second = ref ""
let sequence = ref []
let append_item lst a = lst # [a]
let save () =
try
while true do
let line = input_line stdin in
if k = ref 1
then
begin
first := line;
incr k;
end else
if k = ref 2
then
begin
second := line;
incr k;
end else
begin
sequence := append_item !sequence line;
incr k;
end
done;
None
with
End_of_file -> None;;
let rec command term = match term with
| Print (n) -> print_endline n
| Print_line_in_file (n, f) -> print_endline (nth_line n f)
| Save () -> save ()
;;
EDIT
Error in code:
Save of in_channel:
Error: This pattern matches values of type unit
but a pattern was expected which matches values of type in_channel
Save of unit:
Error: This expression has type 'a option
but an expression was expected of type unit
There are many errors in this code, so it's hard to know where to start.
One problem is this: your save function has type unit -> 'a option. So it's not the same type as the other branches of your final match. The fix is straightforward: save should return (), not None. In OCaml these are completely different things.
The immediate problem seems to be that you have Save () in your match, but have declared Save as taking an input channel. Your current code doesn't have any way to pass the input channel to the save function, but if it did, you would want something more like this in your match:
| Save ch -> save ch
Errors like this suggest (to me) that you're not so familiar with OCaml's type system. It would probably save you a lot of trouble if you went through a tutorial of some kind before writing much more code. You can find tutorials at http://ocaml.org.