【发布时间】:2015-03-11 14:39:14
【问题描述】:
我正在尝试从 Haskell 中的输入中读取行,直到找到非空行。 实际上,我知道如何简单地使用以下代码:
notEmpty [] = return ""
notEmpty (l:xs) = do
s <- l
if s /= "" then return s
else notEmpty xs
getLine' = notEmpty $ repeat getLine
测试(我输入了两个空行,然后是 'foo'):
*> getLine'
foo
"foo"
但是,为了锻炼,我正在尝试使用 Monoids (http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids) 来实现这一点,并尝试模仿 First/getFirst Monoid(请参阅链接)。
我首先在满足我需要的列表上创建了一个 Monoid(连接只保留第一个参数):
newtype FirstSt a = FirstSt { getFirstSt :: [a] }
deriving (Eq, Ord, Read, Show)
instance Monoid (FirstSt a) where
mempty = FirstSt []
FirstSt [] `mappend` x = x
FirstSt s `mappend` _ = FirstSt s
在无限的字符串列表上效果很好(由于懒惰):
> getFirstSt . mconcat . map FirstSt $ ["", "", "foo", "", "bar"] ++ repeat ""
"foo"
但是,我无法让它在 IO Monad 中工作。我尝试了以下方法:
ioFirstSt = (=<<) (return . FirstSt)
getLine'' = getFirstSt <$> mconcat <$> (sequence . map ioFirstSt $ repeat getLine)
哪个类型正确:
*> :t getLine''
getLine'' :: IO [Char]
但是,Haskell 一直希望在将整个列表提供给 mconcat 之前对其进行评估...
有没有办法在 Monoid/Monad 范围内导航时保持懒惰?
【问题讨论】:
-
我相信
IOmonad 的sequence不够懒惰,它会在返回结果之前尝试评估整个IO操作列表。 -
顺便说一句,如果你简单地使用
getLine' = getLine >>= \s -> if s/="" then return s else getLine',你可以避免中间无限列表。尽管如此,幺半群方法还是很有趣的。
标签: haskell io getline string monoids