我有三个函数可以找到列表的第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语句和案例?
I have three functions that find the nth element of a list:
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)In my opinion, the first function is the best implementation because it is the most concise. But is there anything about the other two implementations that would make them preferable? And by extension, how would you choose between using guards, if-then-else statements, and cases?
最满意答案
从技术角度来看,所有三个版本都是等效的。
就是说,我的风格经验是,如果你可以读它,就好像是英文(看作| “当”, | otherwise为“否则”, = “是”或“是”),可能正在做一些正确的事情
if..then..else是当你有一个二进制条件 ,或一个单一的决定,你需要做。 嵌套if..then..else表达式在Haskell中非常罕见,几乎总是使用守卫。
let absOfN = if n < 0 -- Single binary expression then -n else n每个if..then..else表达式都可以被一个if..then..else替换,如果它在一个函数的顶层,这通常是首选的,因为你可以更容易地添加更多的案例:
abs n | n < 0 = -n | otherwise = ncase..of是当您有多个代码路径时 ,每个代码路径都由一个值的结构引导,即通过模式匹配。 你真的很少匹配False 。
case mapping of Constant v -> const v Function f -> map f守卫补充表达式,意味着如果您需要根据值做出复杂的决定, 首先根据输入的结构做出决定, 然后对结构中的值进行决策。
handle ExitSuccess = return () handle (ExitFailure code) | code < 0 = putStrLn . ("internal error " ++) . show . abs $ code | otherwise = putStrLn . ("user error " ++) . show $ codeBTW。 作为一个风格提示,总是在a =或之前创建一个换行符 如果后面的东西= / | 对于一行而言太长,或者因为其他原因使用更多行:
-- 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)From a technical standpoint, all three versions are equivalent.
That being said, my rule of thumb for styles is that if you can read it as if it was English (read | as "when", | otherwise as "otherwise" and = as "is" or "be"), you're probably doing something right.
if..then..else is for when you have one binary condition, or one single decision you need to make. Nested if..then..else-expressions are very uncommon in Haskell, and guards should almost always be used instead.
let absOfN = if n < 0 -- Single binary expression then -n else nEvery if..then..else expression can be replaced by a guard if it is at the top level of a function, and this should generally be preferred, since you can add more cases more easily then:
abs n | n < 0 = -n | otherwise = ncase..of is for when you have multiple code paths, and every code path is guided by the structure of a value, i.e. via pattern matching. You very seldom match on True and False.
case mapping of Constant v -> const v Function f -> map fGuards complement case..of expressions, meaning that if you need to make complicated decisions depending on a value, first make decisions depending on the structure of your input, and then make decisions on the values in the structure.
handle ExitSuccess = return () handle (ExitFailure code) | code < 0 = putStrLn . ("internal error " ++) . show . abs $ code | otherwise = putStrLn . ("user error " ++) . show $ codeBTW. As a style tip, always make a newline after a = or before a | if the stuff after the =/| is too long for one line, or uses more lines for some other reason:
-- 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)更多推荐
发布评论