2012-03-30 2 views
7

나는 형에게하스켈 함수에서 패턴 매칭을 디버그 할 수 있습니까?

data Expr = 
    Const Double 
    | Add Expr Expr 
    | Sub Expr Expr 

을 정의 Eq typeclass의 인스턴스로 선언 : 물론

instance Eq Expr where 
    (Add (Const a1) (Const a2)) == Const b = a1+a2 == b 
    (Add (Const a1) (Const a2)) == (Add (Const b1) (Const b2)) = a1+a2 == b1 + b2 

는 식 Sub (Const 1) (Const 1) == Const 0의 평가가 실패합니다. 어떻게하면 런타임에 패턴 매칭 프로세스를 디버깅하여 실패했는지 확인할 수 있습니까? 하스켈이 어떻게 ==의 주장을 취하고 그 패턴을 통해 걷고 있는지보고 싶습니다. 전혀 가능합니까?

+4

GHC는 컴파일 타임에 문제를 감지 할 수 있습니다. '-Wall'로 컴파일하면 불완전한 패턴에 대해 경고하고 놓친 케이스를 보여줍니다. – hammar

+6

정확한 옵션은'-fwarn-incomplete-patterns'입니다. 어떻게 작동하는지 보려면 http://stackoverflow.com/questions/7883023/algorithm-for-type-checking-ml-like-pattern-matching을 확인하십시오. 그건 그렇고, 당신의 예제에서는'eval :: Expr -> Double' 그리고'x == y = eval x == eval y'라고 쓰는 것이 좋습니다.하지만 여전히 비표준입니다. – sdcvvc

+0

경고에 대해 알고 있습니다. 예를 들어, 필자가 생각해야 할 패턴이 왜 일치해야하는지에 대해 알고 싶습니다. –

답변

2

편집 :

내가 어떤 패턴이 일치하는 것은과 같이, trace 문을 추가하는 것입니다 볼 수있는 가장 쉬운 방법은 찾을 수 ... 질문에 진짜 대답을 제공 :

import Debug.Trace 

instance Eq Expr where 
    (Add (Const a1) (Const a2)) == Const b = trace "Expr Eq pat 1" $ a1+a2 == b 
    (Add (Const a1) (Const a2)) == (Add (Const b1) (Const b2)) = trace "Expr Eq pat 2" $ a1+a2 == b1 + b2 
    -- catch any unmatched patterns 
    l == r = error $ "Expr Eq failed pattern match. \n l: " ++ show l ++ "\n r: " ++ show r 

만약 당신 돈 달리 일치하지 않는 패턴을 잡기 위해 마지막 문장을 포함시키지 않으면 런타임 예외가 발생하지만 어떤 데이터를 얻는 것이 더 유용하다는 것을 알게됩니다. 그런 다음 이전 패턴과 일치하지 않는 이유를 확인하는 것이 일반적으로 간단합니다.

물론 프로덕션 코드에서이 값을 사용하지 않으려 고합니다. 필요하면 흔적을 삽입하고 끝나면 흔적을 제거합니다. CPP를 사용하여 프로덕션 빌드에서 CPP를 둘 수 있습니다.

나는 또한 패턴 매칭이 이것에 대해 잘못된 길로 생각한다고 말하고 싶다. 패턴 수가 많아지면 조합 적 폭발로 끝날 것이며, 관리하기 어려울 정도로 빠르게 커지게됩니다. 예를 들어 ExprFloat 인스턴스를 만들려면 몇 가지 기본 생성자가 더 필요합니다.

대신 아마도 인터프리터 기능이 interpret :: Expr -> Double이거나 적어도 하나를 쓸 수 있습니다.그럼 당신은 패턴 매칭에 의해

instance Eq Expr where 
    l == r = interpret l == interpret r 

를 정의 할 수 있습니다, 당신은 본질적으로 Eq 경우에 해석 기능을 다시 쓰고있어. Ord 인스턴스를 만들려면 해석 기능을 다시 작성해야합니다.

+0

흔적이있는 길입니다. 'Expr'의 몇몇 인스턴스가 함수가 될 수 있기 때문에 double을 계산하는 것은 효과가 없습니다. –

2

일치하는 방법에 대한 몇 가지 예를 보려면 QuickCheck을 참조하십시오. 사용자의 요구에 완벽하게 맞는 재귀 데이터 유형을 생성하고 테스트하는 방법에 대한 예제는 manual (테스트 데이터의 크기)입니다.

-Wall 플래그는 일치하지 않는 패턴 목록을 제공하지만 QuickCheck를 실행하면 주어진 제안을 실패하게 만드는 입력 데이터의 예가 표시됩니다. 예를 들어, Expr에 대한 생성기를 작성하고 quickCheck에 입력하면 Expr이 자체와 동일한 지 확인하는 제안서 prop_expr_eq :: Expr -> Bool이 있는데 일치하지 않는 입력의 첫 번째 예는 매우 빠르게 Const 0.0입니다.

import Test.QuickCheck 
import Control.Monad 

data Expr = 
    Const Double 
    | Add Expr Expr 
    | Sub Expr Expr 
    deriving (Show) 

instance Eq Expr where 
    (Add (Const a1) (Const a2)) == Const b = a1+a2 == b 
    (Add (Const a1) (Const a2)) == (Add (Const b1) (Const b2)) = a1+a2 == b1 + b2 

instance Arbitrary Expr where 
    arbitrary = sized expr' 
     where 
     expr' 0 = liftM Const arbitrary 
     expr' n | n > 0 = 
      let subexpr = expr' (n `div` 2) 
      in oneof [liftM Const arbitrary, 
         liftM2 Add subexpr subexpr, 
         liftM2 Sub subexpr subexpr] 

prop_expr_eq :: Expr -> Bool 
prop_expr_eq e = e == e 

위와 같이 테스트를 실행하면 평등성 테스트가 잘못되었음을 입증 할 수있는 반증을 얻을 수 있습니다. 이것은 조금 지나치게 잔인 할 수도 있지만, 좋은 일을 작성하면 장점은 패턴 매칭뿐만 아니라 임의의 속성을 보는 코드에 대한 단위 테스트를 얻을 수 있다는 것입니다.

*Main> quickCheck prop_expr_eq 
*** Failed! Exception: 'test.hs:(11,5)-(12,81): Non-exhaustive patterns in function ==' (after 1 test): 
Const 0.0 

PS : QuickCheck과 단위 테스트에 대한 또 다른 좋은 독서는 무료로 책 real world haskell입니다.

1

복잡한 패턴을 더 단순한 패턴으로 분해하고 trace을 사용하여 어떤 일이 일어나고 있는지 확인할 수 있습니다. 이 같은 것 :

instance Eq Expr where 
    x1 == x2 | trace ("Top level: " ++ show (x, y1)) True, 
       Add x11 x12 <- x1, 
       trace ("First argument Add: " ++ show (x11, x12)) True, 
       Const a1 <- x11, 
       trace ("Matched first Const: " ++ show a1) True, 
       Const a2 <- x12, 
       trace ("Matched second Const: " ++ show a2) True, 
       Const b <- x2 
       trace ("Second argument Const: " ++ show b) True 
      = a1+a2 == b 

절망적이지만 절망적 인시기에는 필사적 인 조치가 필요합니다. :) 혹시 하스켈에게 익숙해지면 거의하지 말고, 이것을해야합니다.

관련 문제