4

I am currently trying to learn F# better and I am using codingame as an source of programming quizzes.

Most of the quizess involves reading some values from stdin, like the first ten values on stdin will be ints, the next five will be strings.

Currently, I am using this function to read the data, but it feels very "un-f#".

let N = 5
let Reader i =
    Console.In.ReadLine()

let words = [0..N-1] |> Seq.map Reader 
CaringDev
  • 8,391
  • 1
  • 24
  • 43
Christian Sauer
  • 10,351
  • 10
  • 53
  • 85

2 Answers2

9

From the comments, it is apparent that you'd like the most "F#-native" (what we call "idiomatic F#") way of reading from console.

What you have is idiomatic enough, except functions, by convention, usually start with a lower-case character:

let reader i = Console.ReadLine()

Plus, since you're not using the parameter, you don't have to give it a name:

let reader _ = Console.ReadLine()

If the function is small enough, you can write it inline, anonymously:

let words = [0..N-1] |> Seq.map (fun _ -> Console.ReadLine())

Also, since you're not actually using the index, you can declare the list as 1..N instead of 0..N-1. Looks a bit cleaner.

Finally, F# offers very handy list comprehensions that you can use to achieve better readability:

let N = 5
let words = [for _ in 1..N -> Console.ReadLine()]
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • 1
    Thank you for the very nice answer - I have taken the other as answer, because I like the abillity to directly parse the data, but I liked your explanation more - though call. – Christian Sauer Dec 02 '16 at 06:18
  • 4
    Note the special `stdin` value in F# which can be used as a shortcut here: `stdin.ReadLine()`. There is also a `stdout.WriteLine()`. One nice thing about these is that you don't even need to `open System`. – TheQuickBrownFox Dec 02 '16 at 09:06
8

If I had to read given numbers of given types, I would write something like

open System

let read parser =
    Seq.initInfinite (fun _ -> Console.ReadLine())
    |> Seq.choose (parser >> function true, v -> Some v | _ -> None)

which can then be used

let ints = read Int32.TryParse
let ``ten floats`` = read Double.TryParse |> Seq.take 10

Note that if the seq is used multiple times, ReadLine() is called again:

let anInt = ints |> Seq.take 1
printfn "%A" anInt
printfn "%A" anInt // need to input an int again

which can be treated by using e.g. List or Seq.cache.

For strings, which never fail, use

let strings = read (fun s -> true, s)

if you have a minimum length requirement:

let potentialPasswords = read (fun s -> s.Length > 10, s)
CaringDev
  • 8,391
  • 1
  • 24
  • 43