Why does function containing Console.ReadLine() not complete?
Asked Answered
B

2

5

I am using Visual Studio 2012, and the function that calls Console.ReadLine() will not execute

let inSeq = readlines ()

in this simple program

open System
open System.Collections.Generic
open System.Text
open System.IO
#nowarn "40"

let rec readlines () =
    seq {
        let line = Console.ReadLine()
        if not (line.Equals("")) then
            yield line
            yield! readlines ()
}

[<EntryPoint>]
let main argv = 
    let inSeq = readlines ()

    0

I've been experimenting and researching this, and cannot see what is probably a very simple problem.

Belier answered 22/9, 2016 at 21:56 Comment(4)
Is there a line on the console for the function to read? It's expecting input, and blocks until it receives a line feed.Whiffletree
I'm not quite sure what you're asking. The execution in the VS 2012 debugger drops to the last line of the program without waiting for input.Belier
I see ... and Reed posted the answer I was working on, but did a better job of it.Whiffletree
To me, all answers are good. If you're willing to help, it's a good thing as Martha is fond of saying. Thanks.Belier
M
7

Sequences in F# are not evaluated immediately, but rather only evaluated as they're enumerated.

This means that your readlines function effectively does nothing until you attempt to use it. By doing something with inSeq, you'll force an evaluation, which in turn will cause it to behave more like you expect.

To see this in action, do something that will enumerate the sequence, such as counting the number of elements:

open System
open System.Collections.Generic
open System.Text
open System.IO
#nowarn "40"

let rec readlines () =
    seq {
        let line = Console.ReadLine()
        if not (line.Equals("")) then
            yield line
            yield! readlines ()
}

[<EntryPoint>]
let main argv = 
    let inSeq = readlines ()

    inSeq 
    |> Seq.length
    |> printfn "%d lines read"

    // This will keep it alive enough to read your output
    Console.ReadKey() |> ignore
    0
Medeah answered 22/9, 2016 at 22:9 Comment(2)
Your answer explains a lot of weird behavior I was seeing in the debugger. Thanks.Belier
@Belier Yeah - sequences are IEnumerable<T>, and are "lazy".Medeah
G
1

If you change the sequence to a list in readLines() you don't have to 'activate' the sequence to run the input recursion:

open System
open System.Collections.Generic
open System.Text
open System.IO
#nowarn "40"

let rec readlines () =    
  [let line = Console.ReadLine()
   if not (line.Equals("")) then
     yield line
     yield! readlines ()]

[<EntryPoint>]
let main argv = 
    let inList = readlines ()

    printfn "END"
    Console.ReadLine() |> ignore
    0

An non recursive approach could be something like this:

let readLines _ = 
  List.unfold (fun i -> 
                 let line = Console.ReadLine()
                 match line with
                 | "" -> None
                 | _ -> Some (line, i)) 0
Godbeare answered 25/9, 2016 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.