4

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
prichey
  • 75
  • 1
  • 7

1 Answers1

7

(!=) 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.

camlspotter
  • 8,990
  • 23
  • 27
  • This is helpful, but it didn't solve my problem. I think the issue is that when I make a stream from my in_channel, (in the `line_stream_of_channel` function) I call `input_line channel`, which does consume the line. Is there a way to make a stream (so I can peek) without consuming the line? – prichey Mar 04 '14 at 05:09
  • After some experimenting, I think the problem is in Stream.peek after all. If I do `run in_channel; run in_channel` the program acts exactly how I'd like it to, but if I do `run in_channel; print_string (get_option (Stream.peek line_stream)); run in_channel` (the `get_option` function gets the value from the option peeked in `Stream.peek` as a string), the program consumes the peeked line, and therefore doesn't work. Ideas? – prichey Mar 04 '14 at 05:31
  • Without the actual definition of `run` and a sample data, and your expectation from them, I have no idea. – camlspotter Mar 04 '14 at 05:41
  • I added all the relevant sections of code. The program isn't completed (currently it only prints the data), but I'm only interested in the Stream.peek issue. Thanks for any and all help. – prichey Mar 04 '14 at 05:51
  • 1
    This is not the right way to use the stream. You make a strange race between the stream on demand reader and your own direct accesses to the channel. Once you create a stream, read lines from it. Do not access the channel directly, until you close it by `close_in`. – camlspotter Mar 04 '14 at 06:01
  • Ah, that explains a lot. I'll have to rewrite my functions using the stream rather than `in_channel`, but that definitely helps. Thanks. – prichey Mar 04 '14 at 06:06