ネストした分岐を消す方法(タプルを使う)

 まずはこのコードを見て欲しい。

import Numeric.Extra (intToDouble)

main :: IO ()
main = do
  n <- (readLn :: IO Int)
  xs <- sequence $ replicate n (readLn :: IO Int)
  if n == 3
     then case divTotal xs of
               Just result -> putStrLn $ "succeed: " ++ show result
               Nothing     -> putStrLn "The caluculation is failed"
     else putStrLn "x("

divTotal :: [Int] -> Maybe Int
divTotal [] = Nothing
divTotal xs =
  let (x':xs') = map intToDouble xs
  in truncate <$> foldr (flip (/?)) (Just x') xs'
  where
    -- Safe (/)
    (/?) :: Maybe Double -> Double -> Maybe Double
    Nothing /? _  = Nothing
    Just 0  /? _  = Nothing
    Just x  /? y  = Just $ x / y

これは最初にユーザから整数値nの入力を受け取ってから、n個の整数値の入力を受け取った後に、 nが3であれば3つの整数値を左から右に割り算するプログラムだ。

nが3でなければエラーメッセージAを、 割り算で0除算が起こったらエラーメッセージBを、 全て正しく成功したら結果を表示する。

 しかし見て欲しい。 ifとcaseが醜くネストしている。

if n == 3
  then case divTotal xs of
            Just result -> putStrLn $ "succeed: " ++ show result
            Nothing     -> putStrLn "The caluculation is failed"
  else putStrLn "x("

 皆もこんなネストは見たくないよね? まあこのレベルならともかく、もっとネストしていたら冗談でなく見たくなくなる。

 このようなパターンなら、タプルを使ってネストを減らすことができる。

case (n == 3, divTotal xs) of
  (False, _) -> putStrLn "x("
  (True, Nothing)     -> putStrLn "The caluculation is failed"
  (True, Just result) -> putStrLn $ "succeed: " ++ show result

 もしdivTotal xsでerrorが返される場合でも、Haskellの評価戦略ならこの例をうまくやってくれるはずだ。

筆者プロフィール

my-latest-logo

aiya000(あいや)

せつラボ 〜圏論の基本〜」 「せつラボ2~雲と天使と関手圏~」 「矢澤にこ先輩といっしょに代数!」を書いています!

強い静的型付けとテストを用いて、バグを防ぐのが好き。Haskell・TypeScript。