Haskell新手在这里,试图编写代码来解析数学表达式.码:
isDigit :: Char -> Bool isDigit c = c >= '0' && c <= '9' parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h | p == Nothing = Just([h], ls) -- Digit found <<< ERROR!! | otherwise = Just (h:fst d, snd d) -- Ends in a digit | h == '.' | p == Nothing = Nothing -- Ends in a point | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) -- We don't want multiple dots | otherwise = Nothing -- Not a number, stop looking! where p = parseNumber ls Just d = parseNumber ls -- Float version of p. Not used if p is Nothing
此函数应该采用以数字开头的字符串,并返回与表达式其余部分分开的数字.例:
parseNumber"123.0 + 2"
("123.0","+ 2")
我认为这个嵌套的守卫的语法读得非常好,但它不起作用.对于标记的行,错误读取:
输入"|"时解析错误
Haskell中不允许使用链式防护装置吗?或者我是以某种方式错误地写这个?另外,我有什么替代方法可以简单地链接逻辑?
不,但如果您愿意,可以使用案例:
parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = case () of () | p == Nothing = Just([h], ls) | otherwise = Just (h:fst d, snd d) -- Ends in a digit | h == '.' = case () of () | p == Nothing = Nothing | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) | otherwise = Nothing where p = parseNumber ls Just d = parseNumber ls
或者,如果以类似方式工作,则多路(if True | p1 -> b ; | p2 -> c
).
不,你不能.我们都想要它,但没有人能够提出合理的语法.
当你的函数变得非常复杂并且你不能仅仅使用守卫来支持逻辑时,可以考虑使用抽象控制函数来编写函数:
import Control.Applicative import Control.Monad isDigit :: Char -> Bool isDigit c = c >= '0' && c <= '9' parseNumber :: String -> Maybe (String, String) parseNumber [] = return ("", "") parseNumber (h:ls) = dig <|> dot where -- h is either a digit or a dot p = parseNumber ls dig = do guard (isDigit h) -- ensure h is a digit fmap (\(ds,r) -> (h:ds,r)) p <|> return ([h],ls) -- the alternative between two computations -- either the tail is parsed and h prepended to the result -- or the digit is returned by itself dot = do guard (h == '.') -- ensure h is a dot (ds,r) <- p -- parse the tail guard $ not $ '.' `elem` ds -- ensure there is no dot in the tail return (h:ds,r) -- result
这里使用了Monad
,Functor
和MonadPlus
实例Maybe
来实现的分析逻辑.实际上,这个函数推广到了类型MonadPlus m => String -> m (String, String)
- 这里没有实际使用Maybe
构造函数.
该功能也易于阅读.在带有警卫的版本中发生的情况更为明显.
最近的GHC现在有MultiWayIf
:
{-# LANGUAGE MultiWayIf #-} parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = if | p == Nothing -> Just ([h], ls) | otherwise -> Just (h:fst d, snd d) | h == '.' = if | p == Nothing -> Nothing | not ('.' `elem` (snd d)) -> Just (h:(fst d), snd d) | otherwise = Nothing where p@(~(Just d)) = parseNumber ls
但这最好以不同的方式写,没有偏见。
{-# LANGUAGE MultiWayIf #-} parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = if | Nothing <- p -> Just ([h], ls) -- PatternGuards, on by default | Just d <- p -> Just (h:fst d, snd d) | h == '.' = if | Nothing <- p -> Nothing | Just d <- p, not ('.' `elem` snd d) -> Just (h:(fst d), snd d) | otherwise = Nothing where p = parseNumber ls
而且您最好使用maybe
。
parseNumber :: String -> Maybe (String, String) parseNumber "" = Just ("", "") parseNumber (h:hs) | isDigit h = maybe (Just ([h], hs)) (\(num, rest') -> Just (h:num, rest')) rest | h == '.' = maybe Nothing (\(num, rest') -> if '.' `elem` num then Nothing else Just (h:num, rest') ) rest -- This logic is a bit wonky; it doesn't really work | otherwise = Nothing where rest = parseNumber hs