在Haskell中防守与if-then-else对比案例

时间:2012-02-19 00:38:58

标签: haskell if-statement case

我有三个函数可以找到列表的第n个元素:

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)

在我看来,第一个功能是最好的实现,因为它是最简洁的。但是有没有其他两个实现可以使它们更可取?通过扩展,您如何选择使用警卫,if-then-else语句和案例?

3 个答案:

答案 0 :(得分:112)

从技术角度来看,所有三个版本都是等效的。

话虽如此,我对风格的经验法则是,如果你能够将它看作是英语(将|称为“何时”,将| otherwise称为“否则”并{{1作为“是”或“是”),你可能正在做正确的事。

=适用于一个二元条件或您需要做出的单一决策。嵌套的if..then..else - 表达式在Haskell中非常罕见,并且几乎总是应该使用守卫。

if..then..else

每个let absOfN = if n < 0 -- Single binary expression then -n else n 表达式都可以被一个后卫替换,如果它位于函数的顶层,这通常应该是首选,因为你可以更容易地添加更多的案例:

if..then..else

abs n | n < 0 = -n | otherwise = n 适用于多个代码路径,并且每个代码路径都由 结构的值,即通过模式匹配。您很少在case..ofTrue上匹配。

False

Guards补充case mapping of Constant v -> const v Function f -> map f 表达式,这意味着如果您需要根据值做出复杂的决策,首先根据您输入的结构做出决定,然后< / em>对结构中的值做出决定。

case..of

BTW。作为样式提示,如果handle ExitSuccess = return () handle (ExitFailure code) | code < 0 = putStrLn . ("internal error " ++) . show . abs $ code | otherwise = putStrLn . ("user error " ++) . show $ code / {{1之后的内容,请=之后或|之前的新内容对于一行来说太长了,或者出于其他原因使用了更多行:

=

答案 1 :(得分:22)

我知道这是关于显式递归函数的样式的问题,但我建议最好的样式是找到一种方法来重用现有的递归函数。

nthElement xs n = guard (n > 0) >> listToMaybe (drop (n-1) xs)

答案 2 :(得分:1)

这只是一个订购问题,但我认为它非常易读并且与警卫具有相同的结构。

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)

最后的其他不需要,如果没有其他可能性,那么函数应该有“最后的案例”以防你错过任何东西。