简体   繁体   中英

Haskell, how to replace nested if with a case-of statement?

I'm starting to learn haskell and I'm solving some simple programming challenges. So my current problem right now is just printing some text based on the value being passed:

main = do
  n <- readLn :: IO Int
  if n `mod` 2 > 0
      then putStrLn "Weird"
      else if n > 1 && n < 6
          then putStrLn "Not Weird"
          else if n > 5 && n < 21
              then putStrLn "Weird"
              else putStrLn "Not Weird"

However I think this is fairly ugly, is there someway I could replace the nested if structure with a more elegant pattern matching?

I've tried:

main :: IO()
main = do
    n <- readLn :: IO Int
    case n of
        n `mod` 2 /= 0 -> putStrLn "Weird"
        n >= 2 && N <= 5 -> putStrLn "Not Weird"
        n >= 6 && N <= 20 -> putStrLn "Weird"
        n > 20 -> putStrLn "Not Weird"

but I get a compile error.

Edit: Final solution

check :: Int -> String
check n
    | (n `mod` 2 /= 0) = "Weird"
    | (n >= 2) && (n <= 5) = "Not Weird"
    | (n >= 6) && (n <= 20) = "Weird"
    | otherwise = "Not Weird"

main :: IO()
main = do
    n <- readLn :: IO Int
    putStrLn (check n)

You need to use guards if you have boolean expressions (conditions), instead of actual patterns.

main :: IO()
main = do
    n <- readLn :: IO Int
    case () of
       _ | n `mod` 2 /= 0 -> putStrLn "Weird"
         | n >= 2 && n <= 5 -> putStrLn "Not Weird"
         | n >= 6 && n <= 20 -> putStrLn "Weird"
         | n > 20 -> putStrLn "Not Weird"

Consider using otherwise in the last case: assuming you want to catch everything else, it's faster and suppresses a GHC warning about non-exhaustiveness.

A "multiway if" can also be used as an alternative, but it requires an extension.

A case expression [Haskell-report] deals with patterns. An expression like n `mod` 2 /= 0 is not a pattern.

I propose to define a helper function, and use guards :

tmp :: Int -> String
tmp n | n `mod` 2 /= 0 = "Weird"
      | n >= 2 && N <= 5 = "Not Weird"
      | n >= 6 && N <= 20 = "Weird"
      | n > 20 = "Not Weird"
      | otherwise = …

You will still need to define a value for the otherwise case, in case n <= 0 .

We can then use this with:

main = do
    n <- readLn
    putStrLn (tmp n)

"multiway if" was mentioned in chi's answer but not explained and I think it's useful to know about for such cases. Enabling the MultiWayIf extension (eg by writing {-# LANGUAGE MultiWayIf #-} at the top of the file) allows you to write

if | (n `mod` 2 /= 0) -> putStrLn "Weird"
   | (n >= 2) && (n <= 5) -> putStrLn "Not Weird"
   | (n >= 6) && (n <= 20) -> putStrLn "Weird"
   | otherwise -> putStrLn "Not Weird"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM