求Haskell函数expand :: String -> [(Char,Int)]:字符数字串转字符整数对列表
嘿,这个需求我之前也碰到过!要实现expand :: String -> [(Char,Int)]来把"a5b3c2"这类字符串转成字符数字对列表,其实可以从字符串的结构入手——它本质就是单个非数字字符 + 一串数字重复组成的序列对吧?
基础递归实现(快速上手)
最直接的方式就是用递归遍历字符串:每次取第一个字符,然后提取后面跟着的所有连续数字,把数字转成Int后组成元组,再递归处理剩下的部分。代码如下:
import Data.Char (isDigit) expand :: String -> [(Char, Int)] expand [] = [] expand (char:remaining) = (char, read numChars) : expand rest where -- 把剩余字符串拆成数字部分和后续字符部分 (numChars, rest) = span isDigit remaining
来测试你的示例:
expand "a5b4c2" -- 返回 [('a',5),('b',4),('c',2)] expand "d9d3" -- 返回 [('d',9),('d',3)]
不过这个版本有个小问题:如果输入不合法(比如结尾是单独字符"a",或者数字部分是空),read会直接抛出错误。如果需要更健壮的错误处理,可以改成返回Maybe类型:
健壮版(带错误处理)
import Data.Char (isDigit) import Text.Read (readMaybe) expand :: String -> Maybe [(Char, Int)] expand [] = Just [] expand (char:remaining) = case span isDigit remaining of -- 字符后面没有数字,输入无效 ([], _) -> Nothing (numChars, rest) -> do num <- readMaybe numChars restPairs <- expand rest return $ (char, num) : restPairs
现在如果输入"a",会返回Nothing而不是崩溃,更适合生产环境。
进阶:用解析器组合子(Parsec)
如果你的场景需要更复杂的输入验证,比如严格区分字母和数字,或者处理更复杂的格式,用Parsec这类解析器组合子会更优雅:
import Text.Parsec import Text.Parsec.String (Parser) -- 定义单个字符数字对的解析器 pairParser :: Parser (Char, Int) pairParser = do -- 匹配一个非数字字符(如果只需要字母可以换成letter) char <- noneOf ['0'..'9'] -- 匹配一串数字并转成Int num <- read <$> many1 digit return (char, num) -- 扩展整个字符串 expand :: String -> Either ParseError [(Char, Int)] expand = parse (many pairParser) "expand input"
这个版本会返回Either ParseError [(Char,Int)],如果输入不合法,会告诉你具体哪里出错了,比如输入"a5x"会提示unexpected end of input,因为x后面没有数字。
内容的提问来源于stack exchange,提问作者Hamza Halabi
相关产品推荐
相关产品推荐

