尝试从io monad中从爪哇扫描仪阅读时,无限循环
我试图为猫效应课程做一个练习,其中要求我逐行阅读文档,并等待两行之间的一定时间。
老师的解决方案如下:
def readLines(sc: Scanner): IO[Unit] =
if sc.hasNextLine then IO(sc.nextLine).debug *> IO.sleep(1.second) *> readLines(sc)
else IO.unit
但是,我试图从上面的同一功能创建一个尾部回复功能,看起来像这样:
def readLines(sc: Scanner): IO[Unit] =
@tailrec
def tailReader(accum: IO[Unit]): IO[Unit] =
if sc.hasNextLine then tailReader(accum *> IO(sc.nextLine).debug *> IO.sleep(1.second))
else accum
tailReader(IO.unit)
end readLines
问题是,第一个实现确实是按行读取行并完成的。另一方面,我的实现从未完成,甚至从未读过文档的第一行,然后是OutofMemoryError。
我的问题是,有什么办法可以以一种尾巴回报和纯粹功能性的方式实施第一个功能?
我知道有一种方法可以通过使用>>
运算符而不是*>
一个来制作第一个实现堆栈安全。但是,这不是我要寻找的,而是我希望能够使用@TailRec
注释。
我能够部分解决该错误的方式是在功能开头创建一个值,该值将当前行从扫描仪中保存到扫描仪,这样解决了问题:
def readLines(sc: Scanner): IO[Unit] =
@tailrec
def tailReader(accum: IO[Unit]): IO[Unit] =
val currentLine = sc.nextLine
if sc.hasNextLine then tailReader(accum *> IO(currentLine).debug *> IO.sleep(1.second))
else accum >> IO(currentLine).debug.void
tailReader(IO.unit)
end readLines
尽管这解决了问题,但我很确定该解决方案不是纯粹的功能。
如果您能给我解决这个问题,我将非常感激,我才开始了解效果世界,您的帮助将派上用场!
对于其他一切,祝您有美好的一天。
I was trying to do an exercise for a Cats Effect course in which I was required, among other things, to read a document line by line and wait a certain amount of time between lines.
The teacher's solution was the following:
def readLines(sc: Scanner): IO[Unit] =
if sc.hasNextLine then IO(sc.nextLine).debug *> IO.sleep(1.second) *> readLines(sc)
else IO.unit
However, I attempted to create a tail-recursive function from the same function above, and it looked something like this:
def readLines(sc: Scanner): IO[Unit] =
@tailrec
def tailReader(accum: IO[Unit]): IO[Unit] =
if sc.hasNextLine then tailReader(accum *> IO(sc.nextLine).debug *> IO.sleep(1.second))
else accum
tailReader(IO.unit)
end readLines
The problem is, the first implementation does read line by line and finishes. On the other hand, my implementation never finishes and never reads even the first line of the document, and is then followed by an OutOfMemoryError.
My question is, is there any way to implement the first function in a way that is tail-recursive and purely functional?
I know there is a way to make the first implementation stack-safe by using the >>
operator instead of the *>
one. However that's not what I'm looking for, instead I'm looking to be able to use the @tailrec
annotation.
The way I was able to partially fix the error was to create a value at the beginning of the function that saves the current line from the scanner, like this:
def readLines(sc: Scanner): IO[Unit] =
@tailrec
def tailReader(accum: IO[Unit]): IO[Unit] =
val currentLine = sc.nextLine
if sc.hasNextLine then tailReader(accum *> IO(currentLine).debug *> IO.sleep(1.second))
else accum >> IO(currentLine).debug.void
tailReader(IO.unit)
end readLines
Although this solved the problem, I am pretty sure that the solution is not purely functional.
I would be very grateful if you could give me a hand with this problem, I am just starting to understand the world of effects, and your help would come in handy!
For everything else, have a really nice day.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
一些澄清:
>>
和*>
:flatmap
进行堆栈安全递归,因为 monad 是在猫中设计的。我将在以下示例中与
flatmap
交换>>
和*>
因为已经保证了堆栈安全。现在,这里有什么问题
我们正在检查
sc.hasnextline
,而未评估sc.nextline
。IO(sc.nextline)
在这一点上只是一个描述,什么都没评估。使
flatmap
链的递归调用部分解决此问题:然后,递归方法还有另一个小问题。
tailreader(..)
仅在检查下一行后才返回IO。使用
io.suspend
与评估完全分开的定义。现在
tailReader(io.unit)
甚至一次返回IO,而无需调用hasnextline
甚至一次。Some clarifications:
>>
and*>
:flatMap
because of how Monad is designed in Cats.I'll swap
>>
and*>
withflatMap
in the following examples and drop@tailrec
as stack safety is already guaranteed.Now what's wrong here
We're checking
sc.hasNextLine
continually while not evaluatingsc.nextLine
.IO(sc.nextLine)
is only a description at this point, nothing is evaluated.Make the recursive call part of the
flatMap
chain to solve this:then there's another small issue with the recursive method.
tailReader(..)
returns an IO only after it checks for next line once.Use
IO.suspend
to completely separate definition from evaluation.Now
tailReader(IO.unit)
returns the IO without callinghasNextLine
even once.