Haskell 读取多行用户';s输入

Haskell 读取多行用户';s输入,haskell,io,Haskell,Io,我想懒洋洋地阅读用户输入,然后一行一行地处理它。但是,如果用户在一行末尾加上,(逗号)和任意数量的空格(包括零),我想让他有机会在下一行完成输入 这是我得到的: import System.IO import Data.Char chop :: String -> [String] chop = f . map (++ "\n") . lines where f [] = [] f [x] = [x] f (x : y : xs) = if

我想懒洋洋地阅读用户输入,然后一行一行地处理它。但是,如果用户在一行末尾加上
(逗号)和任意数量的空格(包括零),我想让他有机会在下一行完成输入

这是我得到的:

import System.IO
import Data.Char

chop :: String -> [String]
chop = f . map (++ "\n") . lines
    where f [] = []
          f [x] = [x]
          f (x : y : xs) = if (p . tr) x
                           then f ((x ++ y) : xs)
                           else x : f (y : xs)
          p x = (not . null) x && ((== ',') . last) x
          tr xs | all isSpace xs = ""
          tr (x : xs) = x :tr xs

main :: IO ()
main =
    do putStrLn "Welcome to hell, version 0.1.3!"
       putPrompt
       mapM_ process . takeWhile (/= "quit\n") . chop =<< getContents
       where process str = putStr str >> putPrompt
             putPrompt = putStr ">>> " >> hFlush stdout
import System.IO
导入数据.Char
印章::字符串->[字符串]
chop=f。映射(++“\n”)。线
其中f[]=[]
f[x]=[x]
f(x:y:xs)=如果(p.tr)x
然后f((x++y):xs)
其他x:f(y:xs)
px=(非.null)x&(==',').last)x
tr xs|all isSpace xs=“”
tr(x:xs)=x:trxs
main::IO()
主要=
do putStrLn“欢迎来到地狱,版本0.1.3!”
putPrompt
mapM_u过程。takeWhile(/=“退出\n”)。chop=>putPrompt
putPrompt=putStr“>>>”>>hFlush标准输出
对不起,它根本不起作用。一团糟


另外,我想在每个块的末尾保留
\n
字符。目前,我在
行之后用
映射(+“\n”)
手动添加它们

稍微更改
印章的类型如何:

readMultiLine :: IO [String]
readMultiLine = do
              ln <- getLine
              if (endswith (rstrip ln) ",") then
                liftM (ln:) readMultiLine
              else
                return [ln]
但一开始我没抓住要点。这是实际情况

unfoldM :: (Monad m) => (a -> Maybe (m b, m a)) -> a -> m [b]
unfoldM f z = case f z of
            Nothing -> return []
            Just (x, y) -> liftM2 (:) x $ y >>= unfoldM f

main = unfoldM (\x -> if (x == ["quit"]) then Nothing
                      else Just (print x, readMultiLine)) =<< readMultiLine
unfolm::(Monad m)=>(a->Maybe(mb,ma))->a->m[b]
展开m f z=的情况f z
Nothing->return[]
只需(x,y)->liftM2(:)x$y>>=unfolmf
main=unfolm(\x->if(x=[“quit”]),则什么都没有

else Just(print x,readMultiLine))=您可以使用
管道
执行此操作,保留用户输入的惰性

import Data.Char (isSpace)
import Pipes
import qualified Pipes.Prelude as Pipes

endsWithComma :: String -> Bool
endsWithComma str =
    case (dropWhile isSpace $ reverse str) of
        ',':_ -> True
        _     -> False

finish :: Monad m => Pipe String String m ()
finish = do
    str <- await
    yield str
    if endsWithComma str
        then do
            str' <- await
            yield str'
        else finish

user :: Producer String IO ()
user = Pipes.stdinLn >-> finish

要了解有关管道的更多信息,您可以阅读。

对不起,我在评论中写了一些错误的内容,我想现在我明白了您的意图,我应该给出一个更具体的回答。据我所知,核心思想是在循环字符串时需要一个状态缓冲区。您有
f::[String]->[String]
,但您需要一个额外的缓冲字符串才能解决此难题

那么让我假设一个答案如下:

chop = joinCommas "" . map (++ "\n") . lines
然后,
joinCommas
的结构将如下所示:

import Data.List (isSuffixOf)

 -- override with however you want to handle the ",\n" between lines.
joinLines = (++)

incomplete = isSuffixOf ",\n"

joinCommas :: String -> [String] -> [String]
joinCommas prefix (line : rest)
    | incomplete prefix = joinCommas (joinLines prefix line) rest
    | otherwise = prefix : joinCommas line rest
joinCommas prefix [] 
    | incomplete prefix = error "Incomplete input"
    | otherwise = [prefix]

前缀
存储行,直到它不以
“,\n”
结尾,在这一点上它发出前缀并继续与其余行一起。在EOF中,我们处理最后一行,除非该行不完整。

我将
映射头
映射到您的
印章上,以便在程序中实际使用它。你试过了吗?结果:输入永远不会结束…@Mark抱歉,我一开始确实没有抓住要点,以为是解析输入造成了混乱。现在我用
unfolm
函数更新了它。谢谢你的帮助。虽然这个解决方案可行,但它与我最初的设计有太多的偏差。您使用的是自定义函数
readMultiLine
,而不是
getContents
输出的纯转换。我希望整个解决方案是
crop
的一个巧妙实现。也许你的方法更好,我稍后会详细检查。@Mark我想你会发现你不能修改
getContents
返回的
IO a
,因为
IO
的代数结构没有公开。(例如,如果
Monad
是具有已知结构的其他构造,如列表,则可以修改它)我不想修改IO,我想将其结果拆分为字符串。这是绝对可能的。例如,我可以使用
map(++“\n”)。行
,而且工作正常。我认为有可能涉及到更复杂的算法,这样输入字符串只被
'\n'
分割,而不在
'之前,'
。有一点帮助:检查字符串是否以逗号结尾,后跟空格的一种方法是
(“,”
isPrefixOf
)。下降空间。反向
。我不确定lazy IO是否是通过提示符与用户交互的正确工具。我会换成严格的IO。不过,这可能是一个很好的锻炼。@chi,是的,是的,疼痛的锻炼。
chop = joinCommas "" . map (++ "\n") . lines
import Data.List (isSuffixOf)

 -- override with however you want to handle the ",\n" between lines.
joinLines = (++)

incomplete = isSuffixOf ",\n"

joinCommas :: String -> [String] -> [String]
joinCommas prefix (line : rest)
    | incomplete prefix = joinCommas (joinLines prefix line) rest
    | otherwise = prefix : joinCommas line rest
joinCommas prefix [] 
    | incomplete prefix = error "Incomplete input"
    | otherwise = [prefix]