Question

I'm working on a program that iterates over an input file, with a variable number of 'programs', and ending in '0'. My function run works fine if I start it from the top of the file, but for some reason a line is consumed by peeking to see if the next char is '0' (indicating the end of the file).

Here's my code:

let line_stream_of_channel channel =
    Stream.from
      (fun _ ->
         try Some (input_line channel) with End_of_file -> None);;

let in_channel = open_in "dull.in" in
let line_stream = line_stream_of_channel in_channel in
while Stream.peek line_stream != Some "0" do
    run in_channel;
    print_string "...\n";
done;;

From what I've read, Stream.peek shouldn't consume a line, so maybe the problem doesn't come from that, but if not, I can't figure out what's doing it. Any ideas?

Edit Here's the entirety of my program:

let hello c = 
    print_char c;;

let hello_int c =
    print_int c;
    print_char '\n';;

let ios = int_of_string;;

let rec print_string_list = function 
    [] -> print_string "\n"
    | h::t -> print_string h ; print_string " " ; print_string_list t;;

let rec print_int_list = function 
    [] -> print_string "\n"
    | h::t -> print_int h ; print_string " " ; print_int_list t;;

let rec append l i =
    match l with
    [] -> [i]
    | h :: t -> h :: (append t i);;

let line_stream_of_channel channel =
    Stream.from
    (fun _ ->
        try Some (input_line channel) with End_of_file -> None);;

let string_to_int_list str_list int_list=
    let len = List.length str_list in
    for i = 0 to len - 1 do
        int_list := append !int_list (ios (List.nth str_list i));
    done;;

let get_option = function
  | Some x -> x
  | None   -> raise (Invalid_argument "Option.get");;

let chomp_line ns in_channel = 
    let s = input_line in_channel in
    let len = String.length s in
    let start_pos = ref 0 in
    for i = 0 to len do
        if i == len then
            let word = String.sub s !start_pos (i - !start_pos) in
            ns := append !ns word;
        else if s.[i] == ' ' then
            let word = String.sub s !start_pos (i - !start_pos) in
            ns := append !ns word;
            start_pos := i + 1;
    done;;

let run in_channel = 

    let ns = ref [] in
    chomp_line ns in_channel;
    let n = ios (List.nth !ns 0) in
    let p = ios (List.nth !ns 1) in
    let s = ios (List.nth !ns 2) in
    print_string "num dulls: "; hello_int n;
    print_string "num programs: "; hello_int p;
    print_string "num state transitions: "; hello_int s;

    let dull_sizes = ref [] in
    chomp_line dull_sizes in_channel;
    let int_dull_sizes = ref [] in
    string_to_int_list !dull_sizes int_dull_sizes;
    print_string "size of dulls: "; print_int_list !int_dull_sizes;

    let program_sizes = ref [] in
    let program_dulls = ref [] in
    for i = 0 to p - 1 do
        let program = ref [] in
        chomp_line program in_channel;
        program_sizes := append !program_sizes (List.nth !program 0);
        program_dulls := append !program_dulls (List.nth !program 1);
    done;
    let int_program_sizes = ref [] in
    string_to_int_list !program_sizes int_program_sizes;
    print_string "program sizes: "; print_int_list !int_program_sizes;
    print_string "program dulls: "; print_string_list !program_dulls;

    let transitions = ref [] in
    chomp_line transitions in_channel;
    let int_transitions = ref [] in
    string_to_int_list !transitions int_transitions;
    for i = 0 to s - 1 do
        hello_int (List.nth !int_transitions i)
    done
;;

let in_channel = open_in "dull.in" in
let line_stream = line_stream_of_channel in_channel in
while Stream.peek line_stream <> Some "0" do
    run in_channel;
done;;

And here's a sample input:

2 2 3
500 600
100 A
200 B
2 1 2
5 4 8
100 400 200 500 300
250 AC
360 ACE
120 AB
40 DE
2 3 4 -3 1 2 -2 1
0
Was it helpful?

Solution

(!=) is physical (pointer) inequality, and the test fails to detect your end mark 0. When 0 is peeked, Stream.peek returns Some 0, but it is a different entity from Some 0 of the right hand of the inequality check, and therefore the loop never terminates until it crashes at EOF.

The following demonstrates what is happening:

# Some 0 != Some 0;;
- : bool = true
# let x = Some 0 in x != x;;
- : bool = false

Use (<>), structural inequality here. Except it and the omitted run_in_channel part, the code works fine for me.

A golden rule: do not use physical equality (==) and (!=) unless you really need them. Normally, stick to structural equalities (=) and (<>).

-- edit --

There was another issue in the code which was not originally revealed.

Once you create a stream from an in_channel. Do not touch it by yourself, until you want to close it by close_in! Let the stream the only reader of it.

The benefit of the stream is that once created, you are freed from taking care of when the actual readings happen. You could still access the channel directly, but it just ruins the benefit completely. Just do not do it. Use Stream.next or Stream.peek instead of input_line in your run.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top