获取[Char]而不是IO()字符串

时间:2017-02-07 11:46:35

标签: string haskell

我是Haskell的新手。我正在读取输入字符串(a),并且当我在里面找到一个字符(e)时想要返回一个字符串。现在我的整个源代码:

a = "b,b,b,b/b,b,b/b,b,b,b/e,e,e/w,w,w,w/w,w,w/w,w,w,w"
n = length a

simpleCase n =
    case n of
        'a' -> "hey cutie"

eLoopCase i =
    if i < n
        then do
            let char = a !! i
            case char of
                'e' -> putStr $ "(" ++ "found an e" ++ "),"
                'w' -> return ()
                'b' -> return ()
                ',' -> eLoopCase (i+1)
                '/' -> eLoopCase (i+1)
            if (char == ',' || char == '/') == False
                then eLoopCase (i+1)
                else return ()
        else return ()

simpleCase给了我一个字符串,但eLoopCase给了我一个IO()。它有效,但是我希望eLoopCase能够返回一个String,这样我就可以开始工作了。

:t simpleCase 
simpleCase :: Char -> [Char]
:t eLoopCase 
eLoopCase :: Int -> IO ()

我知道这与monad then doputStr有关,这是我的理解结束的地方。删除do会产生解析错误。

3 个答案:

答案 0 :(得分:4)

eLoopCase没有返回任何内容,因为您在所有&#34;结尾处都有return ()&#34;它的。我认为你是在这样的事情之后(注意将当前字符附加到x == 'w' || x == 'b'分支中的递归调用的结果):

a :: String
a = "b,b,b,b/b,b,b/b,b,b,b/e,e,e/w,w,w,w/w,w,w/w,w,w,w"

eLoopCase :: String -> IO String
eLoopCase [] = return []
eLoopCase (x:xs)
  | x == 'e' = do
      putStrLn "(found an e)"
      return [x]
  | x == ',' || x == '/' =
      eLoopCase xs
  | x == 'w' || x == 'b' = do
      rest <- eLoopCase xs
      return (x : rest)
  | otherwise = do
      putStrLn ("Encountered invalid character: " ++ show x)
      return []

您的代码存在一般问题:

  • 你写的程序风格非常重要。这是我敦促你在编写Haskell时试图摆脱的东西。
  • 首选使用!!索引的列表上的模式匹配。

我应该注意到我提供的功能有其自身的缺陷。它预先假定提供给它的字符串只包含几个不同的字符。改进它的一种方法是向守卫添加otherwise分支。 (编辑:添加到上面的代码段)

我认为值得指出的是,这个功能真的不需要依赖于IO来工作。看看其他&#34;纯粹&#34;执行错误处理/报告的替代方法,例如EitherMaybeWriter

答案 1 :(得分:3)

我很高兴您决定尝试学习Haskell。你可以比eLoopCase做得更好。我会告诉你怎么做,但首先我会解释你在原问题中遇到的问题以及你是如何解决的。

正如最初编写的那样,eLoopCase的类型为Int -> IO (),这意味着它是一个函数,它接受Int并返回一个输入输出动作。 IOW,该类型告诉您,如果您给它一个数字,它将执行某事。如果你查看代码,你可以看到某些东西只是打印出字符串,但它几乎可以是任何东西。

在你的回答中,你重写eLoopCase直接通过++运算符构造字符串,而不是通过putStr将它们打印到终端。这就是你的重写函数具有它所具有的类型的原因。它还解释了为什么你不再需要return ()语句,这些语句是&#34;在返回()值时不做任何事情&#34; IO行动。

我希望稍微澄清一下。也就是说,eLoopCase可以得到极大改善。

当控制逻辑与数据结构匹配时,Haskell程序被有效地写入。例如,该程序正在迭代列表。列表被定义为数据,它是一个空列表或一个元素和更多列表,如下面的声明中所示。

data List a = []
            | a : List a

因此,遍历列表的程序将基于这两个构造函数的决策。例如(使用String的同义词[Char]),eLoopCase可以重写为

eLoopCase' :: String -> String
eLoopCase' []     = ""
eLoopCase' (a:as) = evalCase a ++ eLoopCase' as

evalCase a = case a of
  'e' -> "(" ++ "found an e" ++ "),"
  'w' -> ""
  'b' -> ""
  ',' -> ""
  '/' -> ""
  _   -> ""

注意eLoopCase'需要输入a作为输入,因为它不再硬编码到函数体中 - 这是一件好事。它也消除了索引i以及因使用!!而产生的错误(尝试调用eLoopCase (-1))。

练习编写eLoopCase'之类的递归函数是一个好的开始。编程的演变是看看你打算用循环做什么并应用适当的模式。

例如,由于您要通过某个函数评估列表中的每个元素,请使用map。具体来说,map evalCase从字符列表转到字符串列表,然后使用concat将所有这些列表附加在一起:

eLoopCase'' = concat . map evalCase

答案 2 :(得分:0)

其实我已经回答了我自己的问题。以下似乎有效:

a = "b,b,b/b,b,b/b,b,b,b/e,e,e/w,w,w,w/w,w,w/w,w,w,w"
n = length a

eLoopCase i =
    if i < n
        then
            case a !! i of
                'e' -> "(" ++ "found an e" ++ ")," ++ eLoopCase (i+1)
                'w' -> "" ++ eLoopCase (i+1)
                'b' -> "" ++ eLoopCase (i+1)
                ',' -> eLoopCase (i+1)
                '/' -> eLoopCase (i+1)
        else ""

我仍然对发生的事感到好奇。