从stdin读取的最佳方式是什么?

4

我目前正在努力学习F#,并使用codingame作为编程测试题的源。

大多数测试题都涉及从stdin读取一些值,例如前十个值将是整数,接下来的五个值将是字符串。

目前,我正在使用这个函数来读取数据,但感觉它非常不符合F#的风格。

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

let words = [0..N-1] |> Seq.map Reader 

1
你有什么问题? - Mark Seemann
@MarkSeemann 有没有更好的方法从标准输入读取N次? - Christian Sauer
1
通常我会这样写 let words = [for _ in 1...N -> Console.ReadLine()] 或类似的代码(根据不同的游戏)。但我同意@MarkSeemann的观点:问题描述不够清晰。 - Fyodor Soikin
5
“最好的方式”取决于你对“最好”的定义。是最高效的?代码最短的?最可靠的?还是其他什么? - Fyodor Soikin
@FyodorSoikin Python有一些称为“pythonic”的东西,例如本地代码。什么是最“本地”的F#代码? - Christian Sauer
更符合面向对象的风格是将所有东西都命名为名词,所以你可以用 read 来代替 Reader - TheQuickBrownFox
2个回答

9

从评论中可以看出,您希望找到最符合“F#原生”(我们称之为“惯用F#”)的从控制台读取输入的方法。

您现在的写法已经足够符合惯用方式了,除了函数通常按照惯例以小写字符开头:

let reader i = Console.ReadLine()

此外,由于您不使用该参数,因此无需为其命名:
let reader _ = Console.ReadLine()

如果函数足够小,您可以内联匿名编写它:
let words = [0..N-1] |> Seq.map (fun _ -> Console.ReadLine())

此外,由于您实际上并未使用索引,因此可以将列表声明为1..N而不是0..N-1。看起来更清晰。
最后,F# 提供了非常方便的列表推导式,您可以使用它们来获得更好的可读性:
let N = 5
let words = [for _ in 1..N -> Console.ReadLine()]

1
非常感谢您的回答 - 我选择了另一个作为答案,因为我喜欢直接解析数据的能力,但我更喜欢您的解释 - 虽然很棒。 - Christian Sauer
4
请注意F#中特殊的stdin值,可以在此处用作快捷方式:stdin.ReadLine()。还有一个stdout.WriteLine()。这些操作的好处之一是您甚至不需要打开System - TheQuickBrownFox

8
如果我需要读取给定类型的给定数字,我会写出类似以下的代码:
open System

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

这样就可以使用了。

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

请注意,如果多次使用 seq,将再次调用 ReadLine()
let anInt = ints |> Seq.take 1
printfn "%A" anInt
printfn "%A" anInt // need to input an int again

可以使用ListSeq.cache来处理它们。

对于永远不会失败的字符串,请使用

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

如果您有最小长度要求:

let potentialPasswords = read (fun s -> s.Length > 10, s)

我非常喜欢你展示的组合,谢谢! - Christian Sauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接