我是F#的新手,如果我在错误的地方询问,请告诉我。
我有以下阅读文件的序列
let readFile (filePath : string) = seq {
let reader = new StreamReader(filePath)
while not reader.EndOfStream do
yield reader.ReadLine ()
}
我了解了管道转发操作符以及代码看起来如此功能和易于阅读的内容,我试图将上面的内容转换为使用管道转发操作符,换句话说,我想要向前发疯狂。这是我到目前为止所写的内容。请告诉我我做错了什么,或者我是否应该在这种情况下使用它。
let readFilePipe (filePath : string) = seq {
filePath
|> new StreamReader
|> while not reader.EndOfStream do
yield reader.ReadLine()
}
上面的代码不会编译。此外,如果你有一个不应该使用Pipe-forwarding操作符的场景列表,如果你分享它们,我会很高兴。
答案 0 :(得分:3)
这里有两个悬而未决的问题:
StreamReader
关键字以良好的方式处理use
。因此,管道在这里似乎毫无用处。最短的代码是:
let readFile (filePath : string) = seq {
use reader = new StreamReader(filePath)
while not reader.EndOfStream do
yield reader.ReadLine()
}
TL; DR
管道运算符无法有效地与类构造函数一起使用的原因是构造函数通常会被重载,因此如果它被支持,它可能会破坏类型推断。请参阅有关此问题的其他问题:one,two,three。
此代码段显示了问题:
type A (x:int) = class end
let a1 = new A 5 // works
let a2 = 5 |> new A // does not compile: Incomplete structured construct
// at or before this point in expression
如果你真的,真的希望管道与类构造函数一起使用,你可以编写一个小函数或扩展方法(如果类不是sealed
),但同样,它们在你的因为需要使用use
关键字。
// plain function
let makeStreamReader (filePath : string) = new StreamReader(filePath)
// and use
let sr1 = filePath |> makeStreamReader
// extension method
type System.IO.StreamReader with
static member Make (filePath : string) = new StreamReader(filePath)
// and use
let sr2 = filePath |> StreamReader.Make
答案 1 :(得分:2)
这很好:
filePath
|> new StreamReader
这不是:
|> new StreamReader
|> while not reader.EndOfStream do
yield reader.ReadLine()
您需要考虑|>
正在做什么 - while
不是函数,因此在这里使用管道运算符没有意义。
规范的例子是:
filepath
|> new StreamReader
|> processData
|> ...
答案 2 :(得分:1)
这里的问题是符号reader
永远不会被绑定。
|>
运算符取左侧的值并将其应用于右侧的函数:
let (|>) a (f:'a -> 'b) = f a
由于while循环不是一个函数,所以这并不是很有效。换句话说,|>
运算符最适合功能样式代码而非命令式。幸运的是,生成的readFilePipe
函数本身有助于实现功能。
答案 3 :(得分:1)
我没有测试过这个,但是......
let streamToSeq (sr : StreamReader) = seq {
while not sr.EndOfStream do
yield sr.ReadLine()
}
let readFilePipe (filePath : string) =
new StreamReader(filePath)
|> streamToSeq