Is it possible to nest guards in Haskell?

前端 未结 8 909
野的像风
野的像风 2020-12-10 16:52

Haskell newbie here, trying to write code to parse math expressions. Code:

isDigit :: Char -> Bool
isDigit c = c >= \'0\' && c <= \'9\'

pars         


        
相关标签:
8条回答
  • 2020-12-10 17:17

    No, it's not possible. Why not just write it linearly as

    isDigit :: Char -> Bool
    isDigit c = c >= '0' && c <= '9'
    
    parseNumber :: String -> Maybe (String, String)
    parseNumber [] = Just ("", "")
    parseNumber (h:ls)
        -- Digit found
        | isDigit h && p == Nothing = Just([h], ls)
        -- Ends in a digit
        | isDigit h = Just (h:fst d, snd d)
        -- Ends in a point
        | h == '.' && p == Nothing = Nothing
        -- We don't want multiple dots
        | h == '.' && not ('.' `elem` (snd d)) = Just (h:(fst d), snd d)
        -- Not a number, stop looking!
        | otherwise = Nothing
        where
            p = parseNumber ls
            Just d = parseNumber ls -- Float version of p. Not used if p is Nothing
    
    main = print $ parseNumber "123.0 + 2"
    

    If your guards become too involved it's probably a sign that you need to extract a function.

    0 讨论(0)
  • 2020-12-10 17:19

    Using where Just d = ... is dangerous: if you ever access it when p is Nothing your whole program will crash. By doing that, you have to add such checks in your code (as you correctly already did), and be careful not to forget any one of these.

    There are safer ways, such as using case p of Nothing -> ... ; Just d -> ..., using the maybe eliminator, or using functor/applicative/monad tools. Let's use case to keep it simple:

    parseNumber :: String -> Maybe (String, String)
    parseNumber [] = Just ("", "")
    parseNumber (h:ls)
        | isDigit h = case p of
            Nothing -> Just([h], ls)         -- Digit found    <<< ERROR!!
            Just d  -> Just (h:fst d, snd d) -- Ends in a digit
        | h == '.' = case p of
            Nothing -> Nothing                 -- Ends in a point
            Just d | not ('.' `elem` (snd d))
                    -> Just (h:(fst d), snd d) -- We don't want multiple dots
            _       -> Nothing                 -- Not a number, stop looking!
        where 
            p = parseNumber ls
    

    We can also directly pattern match on the subcomponents of d:

    parseNumber :: String -> Maybe (String, String)
    parseNumber [] = Just ("", "")
    parseNumber (h:ls)
        | isDigit h = case p of
            Nothing        -> Just([h], ls)     -- Digit found    <<< ERROR!!
            Just (hs,rest) -> Just (h:hs, rest) -- Ends in a digit
        | h == '.' = case p of
            Nothing -> Nothing           -- Ends in a point
            Just (hs, rest) | not ('.' `elem` rest) 
                    -> Just (h:hs, rest) -- We don't want multiple dots
            _       -> Nothing           -- Not a number, stop looking!
        where 
            p = parseNumber ls
    
    0 讨论(0)
提交回复
热议问题