リストの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つのバージョンはすべて同等です。
そうは言っても、スタイルに関する私の経験則は、英語のように読むことができる場合(|
を "when"として、| otherwise
を "otherwise"として、=
として「is」または「be」)、あなたはおそらく正しいことをしているでしょう。
if..then..else
は、1つの2つの条件がある場合、または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
は、複数のコードパスがあり、すべてのコードパスが構造によって導かれる場合に使用します値の、つまりパターンマッチング経由。 True
とFalse
で一致することはほとんどありません。
case mapping of
Constant v -> const v
Function f -> map f
ガードはcase..of
式を補完します。つまり、値に応じて複雑な決定を行う必要がある場合、firstは構造に応じて決定を行います。入力、およびthenは、構造体の値を決定します。
handle ExitSuccess = return ()
handle (ExitFailure code)
| code < 0 = putStrLn . ("internal error " ++) . show . abs $ code
| otherwise = putStrLn . ("user error " ++) . show $ code
BTW。スタイルのヒントとして、=
の後または|
の後のものが=
の後にある場合は、常に|
の前に改行を入れます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)
これは明示的に再帰的な関数のスタイルに関する質問であることは知っていますが、代わりに既存の再帰関数を再利用する方法を見つけることをお勧めします。
nthElement xs n = guard (n > 0) >> listToMaybe (drop (n-1) xs)
これは単なる順序付けの問題ですが、非常に読みやすく、ガードと同じ構造を持っていると思います。
nthElement :: [a] -> Int -> Maybe a
nthElement [] a = Nothing
nthElement (x:xs) a = if a < 1 then Nothing else
if a == 1 then Just x
else nthElement xs (a-1)
最後の要素は必要ありません。他に可能性がないため、何かを見逃した場合に備えて、関数にも「最後の手段」が必要です。