我是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 do
和putStr
有关,这是我的理解结束的地方。删除do
会产生解析错误。
答案 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 []
您的代码存在一般问题:
!!
索引的列表上的模式匹配。我应该注意到我提供的功能有其自身的缺陷。它预先假定提供给它的字符串只包含几个不同的字符。改进它的一种方法是向守卫添加otherwise
分支。 (编辑:添加到上面的代码段)
我认为值得指出的是,这个功能真的不需要依赖于IO来工作。看看其他&#34;纯粹&#34;执行错误处理/报告的替代方法,例如Either
,Maybe
和Writer
。
答案 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 ""
我仍然对发生的事感到好奇。