0

这是家庭作业的一部分。我需要编写一个从输入读取信息直到空行的函数。在该函数之后,将第一个、第三个、第五个...行符号作为一个字符串,将第二个、第四个...作为另一个字符串。签名是combine :: IO (String , String)

我编写了一个函数,它接受一个列表作为参数,并将 1,3,5.. 放到一个String2,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?

谁能帮我完成我的作业(可选:提供一些提示)?

4

2 回答 2

5

我会给出一些提示来帮助您解决问题。

首先,为您定义的所有函数提供类型是一个巨大的帮助。这让编译器可以立即告诉您一个函数是否没有按照您的想法执行。当您需要将程序的不同部分连接在一起时,它还可以为您提供帮助。

其次,每个函数只解决一个问题是一个非常好的主意。您的 intotwo 函数很好地遵循了这一点,它很好地完成了拆分列表的工作。但是,您的 combine 函数看起来像是在尝试做太多事情,这会使编写变得更加困难。我会将该功能拆分为两个较小的功能。

最后,还有一个非常有用的特殊功能,叫做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 函数更容易编写的函数,所以你应该没问题。

最后,您可以将这三个函数链接在一起以获得结果:

combine :: IO (String, String)
combine = do
  lines <- readLinesUntilEmpty
  let (oddLines, evenLines) = intotwo lines
  return $ (joinLines oddLines, joinLines evenLines)
于 2012-06-10T15:25:21.907 回答
0

由于您需要读取多个字符串,因此您需要递归输入。或者您可以只使用 getContents,但我不确定它是否适合交互式 I/O。像这样的东西:

combine = getContents
          >>= return . intotwo
于 2012-06-10T14:28:33.787 回答