这是家庭作业的一部分。
我需要编写一个函数,从输入读取信息直到空行。之后,该函数将第一个,第三个,第五个...行符号作为一个字符串,第二个,第四个...作为另一个字符串。
签名为combine :: IO (String , String)
我编写了一个以列表作为参数的函数,并将1,3,5 ..添加到String
个2,4,6个符号到另一个String
。功能在这里:
intotwo (x : xs)
= let
(us , vs)
= intotwo xs
in
(x : vs , us)
intotwo _
= ([] , [])
此外,我编写了一个读取输入的代码:它在这里:
combine
= do
lines <- getLine
if null lines
then return ([], [])
else do
linesagain <- combine
return --what should I return?
有人可以帮我完成作业(可选:提供一些提示)?
答案 0 :(得分:5)
我会提供一些提示来帮助您解决问题。
首先,为您定义的所有功能提供类型是一个巨大的帮助。这样,编译器可以立即告诉您函数是否正在执行您认为正在执行的操作。当您需要将程序的不同部分连接在一起时,它还可以帮助您。
其次,每个功能只解决一个问题是一个非常好的主意。你的intotwo函数很好地遵循这个功能,它可以很好地分割列表。然而,你的组合功能似乎试图做太多,这将使它更难写。我会将该函数分成两个较小的函数。
最后,有一个非常有用的特殊功能叫undefined
。这匹配任何类型,并帮助您编写函数。诀窍是从整个函数开始等于未定义(并且编译但在运行时崩溃),并逐步改进函数,直到它完成你想要的并且没有更多的未定义。
首先,我要为intotwo函数添加一个类型签名。类型为[a] -> ([a], [a])
。
接下来,我将编写一个函数,从输入读取行直到它到达空行,然后返回读取的行列表。此函数的类型为:
readLinesUntilEmpty :: IO [String]
readLinesUntilEmpty = undefined
几乎这样做的实现是这样的:
readLinesUntilEmpty = do
nextLine <- getLine
rest <- readLinesUntilEmpty
return (nextLine : rest)
然而,这永远不会停止阅读(注意未检查nextLine是否为空)。
以下函数显示了如何执行此操作(但我将部分实现退出):
readLinesUntilEmpty = do
nextLine <- getLine
case nextLine of
"" -> do
undefined -- TODO fix me
_ -> do
undefined -- TODO fix me
你应该能够从中找出其余部分。
接下来,您的intotwo函数返回两个字符串列表,但您需要再次将它们连接在一起。例如,["this", "that"]
应变为"this\nthat"
。这种函数的类型是[String] -> String
:
joinLines :: [String] -> String
joinLines = undefined -- TODO
这是一个比inttotwo func更容易编写的函数,所以你应该没问题。
最后,您可以将这三个功能链接在一起以获得结果:
combine :: IO (String, String)
combine = do
lines <- readLinesUntilEmpty
let (oddLines, evenLines) = intotwo lines
return $ (joinLines oddLines, joinLines evenLines)
答案 1 :(得分:0)
由于您需要多个字符串读取,因此需要递归输入。或者你可以使用getContents,但我不确定它是否适合交互式I / O. 像这样:
combine = getContents
>>= return . intotwo