1. ホーム
  2. haskell

[解決済み] Haskellのガードとif-then-elseとcaseの比較

2022-08-24 14:27:49

質問

リストのn番目の要素を見つける3つの関数があります。

nthElement :: [a] -> Int -> Maybe a 
nthElement [] a = Nothing
nthElement (x:xs) a | a <= 0 = Nothing
                    | a == 1 = Just x
                    | a > 1 = nthElement xs (a-1)

nthElementIf :: [a] -> Int -> Maybe a
nthElementIf [] a = Nothing
nthElementIf (x:xs) a = if a <= 1
                        then if a <= 0 
                             then Nothing
                             else Just x -- a == 1
                        else nthElementIf xs (a-1)                           

nthElementCases :: [a] -> Int -> Maybe a
nthElementCases [] a = Nothing
nthElementCases (x:xs) a = case a <= 0 of
                             True -> Nothing
                             False -> case a == 1 of
                                        True -> Just x
                                        False -> nthElementCases xs (a-1)

私の意見では、最初の関数が最も簡潔であるため、最良の実装だと思います。しかし、他の2つの実装について、それらが望ましいとするようなことはあるのでしょうか?また、ひいては、ガード、if-then-else文、ケースを使うかどうか、どのように選択するのでしょうか?

どのように解決するのか?

技術的な観点からは、3つのバージョンはすべて同等です。

とはいえ、スタイルに関する私の経験則では、英語のように読むことができれば(read | を "when"のようにします。 | otherwise を "others"として、そして = を "is" または "be" として使用する場合)、おそらく何か正しいことをしているのでしょう。

if..then..else 1つのバイナリ条件 がある場合、または1つの決断が必要な場合に使用します。ネストされた if..then..else -式はHaskellでは非常に珍しく、ガードはほとんどの場合、代わりに使用されるべきです。

let absOfN =
  if n < 0 -- Single binary expression
  then -n
  else  n

すべての if..then..else 式は関数のトップレベルにある場合、ガードで置き換えることができ、これは一般的に好ましいことです。

abs n
  | n < 0     = -n
  | otherwise =  n

case..of 複数のコードパス で、すべてのコードパスが 構造体 によって導かれ、すべてのコードパスはパターンマッチによって導かれます。でマッチすることはめったにありません。 TrueFalse .

case mapping of
  Constant v -> const v
  Function f -> map f

警備員の補足 case..of の式で、値によって複雑な判断をする必要がある場合という意味です。 まず は入力の構造に応じて判断を行い、そして 次に は構造の中の値に対して判断を行う。

handle  ExitSuccess = return ()
handle (ExitFailure code)
  | code < 0  = putStrLn . ("internal error " ++) . show . abs $ code
  | otherwise = putStrLn . ("user error " ++)     . show       $ code

ちなみに。 スタイルのヒントとして、常に = の後、あるいは | の後にあるものは = / | は1行では長すぎるか、他の理由でより多くの行を使用します。

-- NO!
nthElement (x:xs) a | a <= 0 = Nothing
                    | a == 1 = Just x
                    | a > 1 = nthElement xs (a-1)

-- Much more compact! Look at those spaces we didn't waste!
nthElement (x:xs) a
  | a <= 0    = Nothing
  | a == 1    = Just x
  | otherwise = nthElement xs (a-1)