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]