【问题标题】:calling function after input in haskell在haskell中输入后调用函数
【发布时间】:2026-02-14 00:10:01
【问题描述】:

这是家庭作业的一部分。 我需要编写一个从输入读取信息直到空行的函数。在该函数之后,将第一个、第三个、第五个...行符号作为一个字符串,将第二个、第四个...作为另一个字符串。 签名是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?

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

【问题讨论】:

  • 你还没有解释你现在卡在哪里了。您的思路是什么,实现工作成果还缺少什么?
  • else 分支中的返回语句。我的意思是我需要如何调用函数来实现所需的结果。
  • “返回”的表达式(请注意,返回与 C 中的返回不同)必须与函数签名中预期的类型匹配。因此,return 必须将某些内容提升到 IO monad,这意味着您必须返回一个字符串元组。
  • 我知道我应该返回一个字符串元组,但我知道如何完成我的任务。
  • 好吧,您可能需要重新考虑您的方法。另一种方法是先将所有行读入一个列表,然后使用 combine 将该列表划分为两个列表,然后使用 return 返回。

标签: list function haskell input


【解决方案1】:

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

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

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

最后,还有一个非常有用的特殊功能,叫做undefined。这匹配任何类型,并帮助您编写函数。诀窍是从等于未定义的整个函数开始(并在运行时编译但崩溃),并逐步改进该函数,直到它完成您想要的并且没有更多未定义。

首先,我会在 intotwo 函数中添加一个类型签名。类型是[a] -&gt; ([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] -&gt; 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)

【讨论】:

    【解决方案2】:

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

    combine = getContents
              >>= return . intotwo
    

    【讨论】: