简体   繁体   English

读取haskell中的行,直到非空字符串

[英]Reading lines in haskell until non-empty string

I am trying to read lines from input in Haskell until I find a non-empty line. 我试图从Haskell的输入中读取行,直到找到非空行。 Actually, I know how to do it simply using the following code: 实际上,我知道如何使用以下代码来做到这一点:

notEmpty [] = return ""
notEmpty (l:xs) = do
  s <- l
  if s /= "" then return s
             else notEmpty xs

getLine' = notEmpty $ repeat getLine

Test (I typed two empty lines then 'foo'): 测试(我键入两个空行,然后输入“ foo”):

*> getLine'


foo
"foo"

However, for the sake of exercise, I am trying to achieve this using Monoids ( http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids ), trying to mimick the First/getFirst Monoid (see link). 但是,为了便于练习,我尝试使用Monoids( http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids )来实现这一点,尝试模仿First / getFirst Monoid(请参见链接) 。

I first created a Monoid on lists that fits my needs (concatenation only keeps the first argument): 我首先在适合我需要的列表上创建了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

Which works well on a infinite list of strings (thanks to laziness): 在无限的字符串列表上效果很好(由于懒惰):

> getFirstSt . mconcat . map FirstSt $ ["", "", "foo", "", "bar"] ++ repeat ""
"foo"

However, I can't get it to work in the IO Monad. 但是,我无法在IO Monad中使用它。 I tried the following: 我尝试了以下方法:

ioFirstSt = (=<<) (return . FirstSt)
getLine'' = getFirstSt <$> mconcat <$> (sequence . map ioFirstSt $ repeat getLine)

Which has the correct type: 具有正确的类型:

*> :t getLine''
getLine'' :: IO [Char]

However, Haskell keeps wanting to evaluate the whole list before giving it to mconcat ... In there a way to keep laziness while navigating in the Monoid/Monad scope? 但是,Haskell一直希望先评估整个列表,然后再将其提供给mconcat ...在Monoid / Monad范围内导航时,有什么方法可以保持惰性?

Your thinking is excellent. 你的想法很棒。 Monoid is a great structure for this, but sadly as bheklilr points out, sequence is going to perform all the IO regardless. Monoid是一个很好的结构,但是令人遗憾的是,正如bheklilr指出的那样, sequence将执行所有IO。

Theory vs practicality with a nod to Alternative 理论与实践的结合

It would be nice to make instance Monoid (IO String) but we'd have to wrap it in a newtype to get it to compile, but then we'd lose some interoperability with other IO, so let's just write the functions without the instance. 这将是很好使instance Monoid (IO String) ,但我们不得不把它包在newtype得到它来编译,但随后我们就会失去与其他IO一些互操作性,所以我们只写功能,而没有实例。

I like to use <> instead of mappend , but it's taken, and <|> is also taken for Alternative , which is like a Monoid structure for Applicative functors, and you should certainly look into it. 我喜欢使用<>而不是mappend ,但是它被采用了,而<|>也被用于Alternative ,这类似于适用函子的Monoid结构,您当然应该对其进行研究。 I wrote a bit about Alternative in this answer . 我在这个答案中写了一些关于Alternative的文章。

Anyway, let's use <||> and copy the fixity of <> : 无论如何,让我们使用<||>并复制<>的固定性:

infixr 6 <||>

Making a Monoid of a Monad of Eq Monoids 制作一个单子组的等式单子组的单子组

We can make a monoid out of IO String because we can check the value returned to see if it's "" and then do the next action if not. 我们可以使用IO String来制作一个monoid,因为我们可以检查返回的值以查看其是否为"" ,如果不是,则执行下一步操作。 That's equivalent to using == to check whether we have mempty , so we can generalise to IO s as long as s is a Monoid with an Eq instance. 这等效于使用==检查我们是否有mempty ,因此只要s是带有Eq实例的Monoid,我们就可以将IO s推广为IO s Secondly, we don't need it to be IO , we could use any Monad: 其次,我们不需要将其用作IO ,我们可以使用任何Monad:

(<||>) :: (Monoid s, Eq s, Monad m) => m s -> m s -> m s
m <||> n = do
    x <- m
    if x == mempty then n else return x

Notice that that's lazy about computing n - it doesn't bother if we're happy with the output of m . 请注意,这对计算n是懒惰的-如果我们对m的输出感到满意,就不会打扰。 We could then define main = getLine <||> getLine <||> getLine >>= print to give the user up to 3 chances of entering something non-blank for us to print. 然后,我们可以定义main = getLine <||> getLine <||> getLine >>= print为用户提供3次输入非空白内容供我们打印的机会。

Identity and list concatenation 身份和列表串联

Mathematically that's a monoid with identity 从数学上讲,这是具有身份的半身像

msempty :: (Monoid s, Monad m) => m s
msempty = return mempty

Let's also define the equivalent of mconcat :: Monoid s => [s] -> s : 我们还要定义mconcat :: Monoid s => [s] -> s的等效项:

msconcat :: (Monoid s, Eq s, Monad m) => [m s] -> m s
msconcat = foldr (<||>) (return mempty)

Which lets us rewrite as main = msconcat [getLine,getLine,getLine] >>= print 这让我们重写为main = msconcat [getLine,getLine,getLine] >>= print

Lazily combining infinitely many monadic monoids 懒惰地组合无限多个monadic monoid

The real test of laziness here is infinite lists of actions: 懒惰的真正考验是无限的动作列表:

main = msconcat (repeat getLine) >>= print

That works fine, and terminates within a finite time if the user ever does something other than enter nothing. 这可以正常工作,并且如果用户除了输入什么都不做以外,在有限的时间内终止。 Hooray! 万岁!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM