Is it possible to nest guards in Haskell?

2020-02-06 02:13发布

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

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  

This function is supposed to take a string that starts with a number, and returns the number separated from the rest of the expression. Example:

parseNumber "123.0 + 2"

("123.0", " + 2")

I think this nested guards' syntax reads really nicely, but it doesn't work. The error reads, for the marked line:

parse error on input `|'

Are chained guards not allowed in Haskell? Or am I writting this wrongly somehow? Also, what alternatives do I have to chain logic in a simple way?

标签: haskell
8条回答
狗以群分
2楼-- · 2020-02-06 02:44

It is possible to chain guards, with ,. This does basically the same as && in fjarri's answer, but is more versatile when it comes to pattern guards.

What's not possible is nesting guards. Well, in your example that's only actually needed in the first clause. You could write

parseNumber (h:ls)
    | isDigit h
       = if isNothing p
           then Just ([h], ls)        -- Digit found    <<< ERROR!!
           else Just (h:fst d, snd d) -- Ends in a digit
    | h == '.'
    , not ('.' `elem` snd d)
       = Just (h:fst d, snd d)    -- We don't want multiple dots
    | otherwise = Nothing       -- Not a number, stop looking!
查看更多
Rolldiameter
3楼-- · 2020-02-06 02:51

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.

查看更多
登录 后发表回答