1
let separate = fun formula ->
    let rec aux = fun counter begin size ->
    match formula.[begin + size] with
    | '(' -> aux (counter + 1) begin (size + 1)
    | ')' -> if (counter - 1) = 0 then ((String.sub formula (begin + 1) size), (String.sub formula (size + begin + 3) ((String.length formula) - (size + begin + 2)))) else aux (counter - 1) begin (size +1)
    | _ -> aux counter begin (size + 1)
  in aux 0 (String.index formula '(') 0
;;

let main s=
    print_string (fst (separate s))
;;

main "&(A)(B)"

Hi everyone. My problem is, when i execute this ocaml code which consist in separating a string into two, i have this exception : invalid_argument "String.sub / Bytes.sub". And i'm wondering, can you guys help me ?

  • 1
    Since `begin` is a keyword in OCaml, the code you give here is not syntactically valid. You should give a short, self-contained, and valid example that shows your problem. As a general observation, `String.sub` requires the first int argument to be >= 0 and <= the length of the string, it requires the second int to be >= 0, and it requires the sum to be <= the length of the string. – Jeffrey Scofield Apr 21 '17 at 23:21

2 Answers2

2

The other answer and the first comment at the top both pointed out a misused keyword of begin. It's a minor fix, but does not address what Antonio is asking about.

For starters, I am not familiar with what context you are using your function in, I am just going to tell you where your program went wrong in a trace-based approach.

First, I made a few changes to your code, so that it is more readable, and also easier to figure out the stack frame where things went wrong:

let separate = fun formula ->
    let rec aux = fun counter start_position size ->
    match formula.[start_position + size] with
    | '(' -> aux (counter + 1) start_position (size + 1)
    | ')' -> if (counter - 1) = 0 then (
        (Printf.printf "%d %d %d\n" counter start_position size); 
    (
        ((Printf.printf "pass1\n"); (String.sub formula (start_position + 1) size))
        ,
        (String.sub formula (size + start_position + 3) ((String.length formula) - (size + start_position + 2)))
    ) 
        )
            else aux (counter - 1) start_position (size +1)
    | _ -> aux counter start_position (size + 1)
  in aux 0 (String.index formula '(') 0
;;

separate "&(A)(B)";;

(*
1 1 2 
Exception: Invalid_argument "String.sub / Bytes.sub".
Raised at file "pervasives.ml", line 33, characters 25-45
Called from file "string.ml", line 47, characters 2-23
Called from file "//toplevel//", line 10, characters 4-108
Called from file "toplevel/toploop.ml", line 180, characters 17-56
*)

Run this, and you will find out the call that triggered the invalid argument exception is at the call of aux 1 1 2.

Then you can use these codes to test why it went wrong:

let (counter, start_position, size) = (1,1,2) in
let formula = "&(A)(B)" in
(
(size + start_position + 3)
,
(String.length formula) - (size + start_position + 2) 
)       ;;

(*
- : int * int = (6, 2) 
*)

And this is how you know why String.sub does not accept your arguments:

String.sub "&(A)(B)" 6 2;;
(*  Exception: Invalid_argument "String.sub / Bytes.sub".
Called from file "toplevel/toploop.ml", line 180, characters 17-56 *)

And this is as far as I can help you. Debugging in OCaml takes time, but it's doable. Go back to the good o'l printing trick. And I hope this is enough information for you to see why your logic is faulty here. I hope you can figure out the way to fix it according to you problem context.

qzhangjhu
  • 131
  • 1
  • 6
0

Your code does not compile because you are using the keyword begin.

Once modified :

let separate = fun formula ->
    let rec aux = fun counter b size ->
    match formula.[b + size] with
    | '(' -> aux (counter + 1) b (size + 1)
    | ')' -> if (counter - 1) = 0 then ((String.sub formula (b + 1) size), (String.sub formula (size + b + 3) ((String.length formula) - (size + b + 2)))) else aux (counter - 1) b (size +1)
    | _ -> aux counter b (size + 1)
   in aux 0 (String.index formula '(') 0
;;

Now, to get more visibility on exception, add this :

 let () = Printexc.record_backtrace true;;

Compile it (using ocamlbuild) and run it, you will get sth like :

Fatal error: exception Invalid_argument("String.sub / Bytes.sub")
Raised at file "pervasives.ml", line 33, characters 20-45
Called from file "string.ml" (inlined), line 47, characters 2-23
Called from file "sep.ml", line 5, characters 75-153
Called from file "sep.ml" (inlined), line 11, characters 22-34
Called from file "sep.ml", line 15, characters 0-14
Pierre G.
  • 4,346
  • 1
  • 12
  • 25