我想从 STDIN 读取由换行符分隔的字符串列表,直到看到新行并且我想要 IO [String] 类型的操作。这是我将如何使用递归来做到这一点:

myReadList :: IO String
myReadList = go []
where
    go :: [String] -> IO [String]
    go l = do {
                 inp <- getLine;
                 if (inp == "") then
                     return l;
                 else go (inp:l);
                }

然而,这种使用 go 的方法掩盖了可读性,并且是一种非常常见的模式,以至于人们理想地希望将其抽象出来。

因此,这是我的尝试:
whileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
whileM p []     = return []
whileM p (x:xs) = do
    s <- x
    if p s
    then do
        l <- whileM p xs
        return (s:l)
    else
        return []

myReadList :: IO [String]
myReadList = whileM (/= "") (repeat getLine)

我猜这个 whileM 或类似的东西已经有一些默认实现。但是我找不到它。

有人能指出处理这个问题的最自然、最优雅的方法是什么吗?

最佳答案

unfoldWhileM 与您的 whileM 相同,只是它采用一个 Action (不是列表)作为第二个参数。

myReadList = unfoldWhileM (/= "") getLine

10-06 00:55