[haskell] Guards vs. if-then-else vs. Haskell의 케이스

목록의 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 문 및 케이스 사용 중에서 어떻게 선택 하시겠습니까?



답변

기술적 관점에서 세 가지 버전은 모두 동일합니다.

즉, 스타일에 대한 제 경험 법칙은 마치 영어처럼 읽을 수 있다면 ( |“언제”, | otherwise“그렇지 =“또는 “있음”으로 읽음 ) 뭔가를하고있을 것입니다. 권리.

if..then..else당신이있을 때위한 하나 개의 바이너리 조건 , 또는 당신이해야 할 하나의 결정을. 중첩 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표현식을 보완 합니다. 즉, 값에 따라 복잡한 결정을 내려야하는 경우 먼저 입력의 구조에 따라 결정을 내린 다음 구조의 값에 대한 결정을 내립니다.

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

BTW. 스타일 팁으로 / 뒤의 내용 이 한 줄에 너무 길거나 다른 이유로 더 많은 줄을 사용 =하는 |경우 항상 a 뒤 또는 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)


답변

이것이 명시 적으로 재귀 함수에 대한 스타일에 대한 질문이라는 것을 알고 있지만 가장 좋은 스타일은 기존 재귀 함수를 대신 재사용하는 방법을 찾는 것입니다.

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)

마지막 다른 것은 필요하지 않으며 다른 가능성이 없기 때문에 함수도 놓친 경우 “마지막 수단”이 있어야합니다.


답변