我想读取数据,因为它出现在文件中(数据由另一个进程写入)。像“连续阅读”这样的东西。
简单的代码只是将文件读到最后并完成工作而没有问题:
let readStream (r: StreamReader) =
seq {
while not r.EndOfStream do
yield r.ReadLine() }
现在我向它添加以下逻辑:如果它到达流的末尾,它等待一秒钟,然后再次尝试。如果没有出现数据,它会在5次尝试中休眠2秒钟,最多持续5秒钟:
let readStream (r: StreamReader) =
let rec loop waitTimeout waits =
seq {
match waitTimeout with
| None ->
if not r.EndOfStream then
yield r.ReadLine()
yield! loop None 0
else yield! loop (Some 1000) waits
| Some timeout when waits < 5 ->
let waits = waits + 1
printfn "Sleeping for %d ms (attempt %d)..." timeout waits
Thread.Sleep (timeout * waits)
yield! loop None waits
| _ -> ()
}
loop None 0
不幸的是,结果是非尾递归,应用程序很快崩溃了StackOverflow异常。
有人可以帮忙吗?
答案 0 :(得分:1)
在进一步详细说明之前,您是否在启用调试时生成尾调用? (默认情况下,F#不会在调试代码中执行尾调用。)
编辑:嗯,您的返回值最终为seq {...}
,实际上不是loop x y
的值。所以你真的在构建一个嵌套序列,而不是使用尾递归。
尝试以这样的方式重写代码,即不使用嵌套序列,而是在序列中使用尾递归。
答案 1 :(得分:1)
正如丹尼尔指出的那样,根据定义,一个具有序列表达式的主体的函数不是尾递归的,因为任何递归调用都会被延迟。这也保证了调用readStream
不会导致堆栈溢出 - 它只会返回一个不透明的seq<string>
值。因此,堆栈溢出的原因至少部分取决于调用代码。
答案 2 :(得分:1)
嗯,你写的代码也适合我(在F#3.0上)。正如kvb
指出的那样,您的消费者代码是否存在问题?
type Reader(k) =
let mutable n = 0
member __.EndOfStream = n >= k
member __.ReadLine() = n <- n + 1; "OK"
let readStream (r: Reader) =
let rec loop waitTimeout waits =
seq {
match waitTimeout with
| None ->
if not r.EndOfStream then
yield r.ReadLine()
yield! loop None 0
else yield! loop (Some 1000) waits
| Some timeout when waits < 5 ->
let waits = waits + 1
printfn "Sleeping for %d ms (attempt %d)..." timeout waits
System.Threading.Thread.Sleep(timeout * waits)
yield! loop None waits
| _ -> ()
}
loop None 0
let test () =
readStream (Reader 2000000)
|> Seq.length
答案 3 :(得分:0)
我替换了(waitTimeout:TimeSpan选项)+模式匹配(waitTimeout:int)+ if ...然后问题已经消失。它可以在数百万个元素的seqs上工作,在常量RAM中。
糟糕的是我不知道它为什么会起作用。
let readStream (r: StreamReader) =
let rec loop waitTimeout waits =
seq {
if waitTimeout = 0<ms> then
let line = r.ReadLine()
if line <> null then
yield line
yield! loop 0<ms> 0
else yield! loop 1000<ms> waits
elif waits < 5 then
let waits = waits + 1
Thread.Sleep (int waitTimeout * waits)
yield! loop 0<ms> waits
}
loop 0<ms> 0