Hew*_*lff 2 haskell memory-leaks lazy-evaluation
我想出了原始输入问题的以下代码,正如SO Haskell中读到的原始键盘输入所讨论的那样.不幸的是,当我去ghci,运行getAllInput并点击右箭头键时,它永远不会返回.除非我很快就杀了它,它似乎占用了我所有的内存,以便其他应用程序停止响应,我必须重新启动操作系统.在活动监视器中,我可以看到ghc进程的内存快速进入千兆字节.
(1)我认为问题出在递归调用中go,通过hReady之前的调用来懒惰地评估getChar; 这意味着hReady保持返回true并且堆栈永远增长.这看起来有道理吗?
(2)我习惯于很快会导致堆栈溢出异常的语言,所以它不会阻止我工作.有没有一般的方法来防止这种大规模的内存泄漏?也许以内存使用的硬限制开始ghci?
import System.IO
-- For example, should get "\ESC[C" from the user hitting the right arrow key.
getAllInput :: IO [Char]
getAllInput =
let
go :: IO [Char] -> IO [Char]
go chars = do
more <- hReady stdin
if more then go (added chars getChar) else chars
added :: IO [Char] -> IO Char -> IO [Char]
added chars char = do
chars1 <- chars
char1 <- char
return (chars1 ++ [char1])
in do
hSetBuffering stdin NoBuffering
firstChar <- getChar
go (return [firstChar])
Run Code Online (Sandbox Code Playgroud)
我在OS X 10.11.6中运行ghci 7.10.3.我以一些明显的方式清理了代码,基本上遵循类似的SO答案:将getChar调用放在自己的行中可以解决问题.但我想更好地理解这一点,以防它再次咬我.
你有一个无限循环go.如果hReady返回true,则go再次调用,立即hReady再次调用,这当然会返回true,依此类推.你可能认为它added会因为而被运行go (added chars getChar),但它不会; 它只是构建一个IO动作并将其go作为参数传递,但该参数仅在hReady返回时使用False.该名称chars具有误导性 - chars实际上是一个I/O过程,它将在最终运行时返回一个字符列表.
通常,使用monad时,"普通"函数签名如下所示:
:: Foo -> Bar -> Baz -> IO Quuz
Run Code Online (Sandbox Code Playgroud)
也就是说,monad(IO)仅出现在返回值上,而不是参数上.签名就像
:: IO Foo -> IO Bar
Run Code Online (Sandbox Code Playgroud)
通常表示某些高阶正在进行,例如,此函数可能会多次执行其参数或在新的上下文中执行.
我推荐签名
go :: [Char] -> IO [Char]
added :: [Char] -> Char -> IO [Char]
Run Code Online (Sandbox Code Playgroud)
并试图让程序从那里编译.
您还应该尝试更改added为纯函数
added :: [Char] -> Char -> [Char]
Run Code Online (Sandbox Code Playgroud)
因为它实际上没有任何副作用.但是,实现和使用需要稍微改变一下.
| 归档时间: |
|
| 查看次数: |
200 次 |
| 最近记录: |