2009-05-07 9 views
17

"불가능한"경우에도 하스켈에서 철저한 패턴 매칭을 항상하는 것이 좋습니까?"불가능한"경우에도 하스켈에서 철저한 패턴 매칭을 항상하는 것이 좋습니까?

예를 들어, 다음 코드에서 foldr의 "누적 기"에 패턴 일치가 있습니다. 누적 기의 내용을 완벽하게 제어합니다. 생성하기 때문에 (입력으로 전달되지 않고 내 함수 내에서 생성됩니다). 따라서 특정 패턴이 절대로 일치해서는 안된다는 것을 알고 있습니다. "Pattern match (es) is non-exhaustive"오류가 발생하지 않도록 노력한다면 패턴 일치를 배치하여 "이 패턴은 발생하지 않아야합니다."라는 메시지와 함께 오류가 발생합니다. C#의 주장과 매우 ​​비슷합니다. 나는 그곳에서 할 일을 생각할 수 없다.

이 상황에서 어떤 연습을 권하고 싶습니까? 그 이유는 무엇입니까?

gb_groupBy p input = foldr step [] input 
    where 
     step item acc = case acc of 
      []       -> [[item]] 
      ((x:xs):ys)     -> if p x item 
              then (item:x:xs):ys 
              else [item]:acc 

(인터프리터에 의해보고) 일치하지 않는 패턴은 다음과 같습니다 :

경고 : 패턴 일치 (들) 경우의 대안 비 철저한 있습니다 : 패턴 여기

코드입니다 일치하지 않음 : [] : _

+9

여기서 귀찮은 것은 경고 메시지를 없애기 위해 덜 유용한 런타임 오류 메시지를 넣는 경우가 많다는 것입니다. 누락 된 사례를 인정하고 싶지만 기본 런타임 오류 텍스트 (파일/행 번호를 가리킴)를 사용할 수 있습니다. –

+0

그것은 좋은 지적입니다. 나는 현재 그렇게 할 수있는 방법이 없다고 생각한다. 너무 나빴어. –

+0

또한보십시오 http://stackoverflow.com/questions/1882334 – sdcvvc

답변

19

이것은 아마도 다른 어떤 것보다 스타일의 문제 일 것입니다.

gb_groupBy p input = foldr step [] input 
    where 
    step item acc = case acc of 
     []       -> [[item]] 
     (xs:xss)      -> if p (head xs) item 
             then (item:xs):xss 
             else [item]:acc 

패턴 매칭은 다음과 같습니다 경고 : 당신은이 작업을 수행하여이 특별한 경우에 경고를 해결할 수 있습니다

+1

나는이 접근법에 동의합니다. 그것은 당신이 의도적으로 다른 케이스를 다루지 않았다는 것을 나타내는 데 도움이됩니다. 왜냐하면 당신은 단지 잊거나 깨닫기보다는 불가능하다고 생각하기 때문입니다. – newacct

+6

프로그램이 갑자기이 설명적인 메시지로 끝나면 항상 재미 있습니다 : *** 예외 : 불가능한 일이 있습니다! –

+0

그래, 내가 프로그래밍을 처음 접했을 때 생각 나게하고, "너는이 선에 도달해서는 안되다." :) –

10

침묵하는 경우에만 개인적으로, 나는

_ -> error "Impossible! Empty list in step" 

에 둘 것 그리고 나서 완료되고 누적 기 헤드의 빈 목록의 "불가능한"조건은 런타임 오류를 발생 시키지만 경고는 발생시키지 않습니다.

불완전한 패턴 매치의보다 일반적인 문제를 보는 또 다른 방법은 "코드 냄새"로 보는 것입니다. 즉, 차선책 또는 비 하스켈 식으로 문제를 해결하려고한다는 표시입니다. , 우리의 기능을 다시 쓰려고 노력하십시오.

foldr을 사용하여 groupBy를 구현하면 무한한 목록에 적용 할 수 없으며, 이는 하스켈 목록 함수가 의미 상으로 합리적인 곳에서 달성하려고하는 디자인 목표입니다. 고려하십시오

take 5 $ groupBy (==) someFunctionDerivingAnInfiniteList 

처음 5 개 그룹이 w.r.t. 평등은 유한이며 게으른 평가는 종료됩니다. 이것은 엄격하게 평가 된 언어로는 할 수없는 일입니다. 당신이 무한리스트와 함께 작동하지 않는 경우에도, 다음과 같은 기능을 작성하는 것은 긴 목록에 더 나은 성능을 얻을, 또는 List.hs에서

take 5 $ gb_groupBy (==) [1..1000000] 

, GROUPBY는 다음과 같이 구현과 같은 식을 평가할 때 발생하는 스택 오버 플로우를 방지 할 수 있습니다 :

groupBy   :: (a -> a -> Bool) -> [a] -> [[a]] 
groupBy _ []  = [] 
groupBy eq (x:xs) = (x:ys) : groupBy eq zs 
          where (ys,zs) = span (eq x) xs 

이 결과는 필요한 계산의 부분을 평가하는 인터프리터/컴파일러 수있다. span은 첫 번째가 술어를 만족하는 목록의 헤드에서 (연속적인) 요소로 구성되고 두 번째가 목록의 나머지 부분 인 한 쌍의 목록을 생성합니다. 그것은 또한 무한한 목록에서 작동하도록 구현되었습니다.

+0

+1, 통찰력. –

+0

> 이것은 엄격하게 평가 된 언어로 할 수없는 것입니다. 이것은 모든 튜링 언어로 할 수 있습니다. 때로는 그렇게 어렵지도 않습니다. 예를 들어, Java에서도 "무한"목록을 쉽게 모델링 할 수 있습니다. –

+1

튜링 계산이란 어떤 계산을 할 수 있는지를 지정하는 것이지 지정하는 것이 아니라 후자가 내 대답의 분명한 범위를 의미합니다. 물론 자바로 된 Guava 컬렉션 유틸리티와 함께 ​​뭔가를 자갈로 만들거나 더 우아하게 LINQ를 사용하여 무언가를 만들 수 있습니다.그물. 그러나 이것은 Iterable이나 IEnumerable을 구현하는 것이 아니라, 명시 적으로 lazy 계산을 가능하게하는 콜렉션을 필요로합니다. Guava의 AbstractIterator 또는 yield 키워드를 C#에서 구현합니다. Haskell에서 콜렉션을 반환하는 모든 것은 느리게 사용할 수 있습니다. – Christoph

4

필자의 이전 의견을 확인하기 위해 누락 된 사례를 확인했지만 파일/줄 번호와 관련된 유용한 오류가 표시된다는 것을 알았습니다. 그래도 최적화되지 않은 빌드에만 표시되기 때문에 이상적이지 않습니다 (here 참조).

... 
[]:xs -> assert False (error "unreachable because I know everything") 
+0

니스. 후속 조치 주셔서 감사합니다. –

7

케이스 패턴에 대한 철저한 검사가 빠뜨릴 수 없습니다. _이 모든 것을 매칭하기 때문에 최상위 레벨의 케이스에서는 결코 _을 사용하지 않으려 고 노력합니다. 그리고이를 사용하여 완전성 검사의 가치를 저하시킵니다. 목록에서는 중요하지 않지만 사용자 정의 대수 데이터 형식에서는 매우 중요합니다. 새 생성자를 추가하고 누락 된 모든 경우에 컴파일러 barf를 사용할 수 있기를 원하기 때문입니다. 이런 이유로 나는 항상 -Werror을 켜고 컴파일하므로, 사건을 빠뜨릴 수는 없습니다. 관찰

, 코드가

[] : _ -> error "this can't happen" 

내부적으로이 경우 확장 할 수 있습니다, GHC는 error 달리 소스 좌표를 줄 것이다 panic 기능을 가지고 있지만, 나는 구현을 보면서 머리를 만들 수 없습니다 또는 그것의 꼬리.

+2

나는 그것을 가이드 라인으로 정말 좋아한다. C#에서 프로그래밍 할 때, 나는 종종 "철저한 검사"와 같은 것을 원했습니다. 예를 들어 열거 형과 각 열거 형 멤버를 처리하기위한 사례 문이있는 경우 컴파일러에게 "모든 사례를 모두 포함했는지 확인"할 수 있기를 바랍니다. 나는 그런 개념이 하스켈과 다른 기능적 언어의 기초의 일부라는 것을 전혀 몰랐다. –

+0

오 예, 저는 열거와 전환을 사용할 때 항상 유죄라고 생각합니다. 그러나 전체 클래스 계층 구조를 만들고 하나의 진지한 방법을 많은 작은 가상 메서드로 분할하는 것보다 훨씬 간결합니다. –

+0

예. 나는 다형성을 아주 잘 믿어.하지만 그걸 제외하고는 값을 하위 범주 (3, 3 또는 그보다 작지만 홀수, 3 또는 그 이상. 조차). 잘못 표현하면 "경계"오류가 발생합니다. 그러나 컴파일러는 모든 가능한 범위를 포함하려고한다는 것을 알고 있다면 쉽게 알 수 있습니다. 그리고 그것은 많은 함수 언어에서 패턴 매칭이하는 일 중 하나입니다. –

2

유형 시스템은 친구이며 경고는 귀하의 기능에 균열이 있음을 알려주는 것입니다. 가장 좋은 방법은 형식간에보다 깔끔하고 우아한 느낌을주는 것입니다. 보기의

groupBy     :: (a -> a -> Bool) -> [a] -> [[a]] 
groupBy _ []   = [] 
groupBy eq (x:xs)  = (x:ys) : groupBy eq zs 
          where (ys,zs) = span (eq x) xs 
1

내 포인트가 불가능한 경우 정의되지 않은 점이다 :

groupBy의 GHC의 정의를 생각해 보자.
정의되지 않은 경우 우리는이 함수를 사용할 수 있습니다. 교묘하게도 undefined입니다.

_ -> undefined 

을 그리고 거기 당신은 그것이있다 :

의 좋아하여 일치를 완료!