2012-03-30 22 views
7

tôi đã xác định một loạiCó thể gỡ lỗi khớp mẫu trong một hàm Haskell không?

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

và tuyên bố nó như là một thể hiện của 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 

Tất nhiên, việc đánh giá sự biểu hiện Sub (Const 1) (Const 1) == Const 0 sẽ thất bại. Làm thế nào tôi có thể gỡ lỗi tại thời gian chạy quá trình phù hợp với mô hình để phát hiện ra rằng nó không? Tôi muốn xem làm thế nào Haskell có các đối số của == và đi qua các mô hình. Có thể ở tất cả?

+4

GHC có thể phát hiện sự cố lúc biên dịch. Nếu bạn biên dịch với '-Wall', nó sẽ cảnh báo bạn về mẫu không hoàn chỉnh và cho bạn thấy trường hợp nào bạn đã bỏ lỡ. – hammar

+6

Tùy chọn chính xác là '-fwarn-incomplete-patterns'. Kiểm tra http://stackoverflow.com/questions/7883023/algorithm-for-type-checking-ml-like-pattern-matching để xem cách hoạt động của nó. Bằng cách này, trong ví dụ của bạn tôi khuyên bạn nên viết 'eval :: Expr -> Double' và sau đó' x == y = eval x == eval y', nhưng nó vẫn còn không chuẩn. – sdcvvc

+0

Tôi biết về các cảnh báo. Tôi muốn biết, ví dụ, tại sao một mô hình tôi nghĩ nên phù hợp, không. –

Trả lời

2

chỉnh sửa: cung cấp một câu trả lời thực sự cho câu hỏi ...

tôi thấy cách dễ nhất để xem những gì các mẫu được phù hợp là thêm trace báo cáo, như vậy:

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 

Nếu bạn don không bao gồm một tuyên bố cuối cùng để nắm bắt bất kỳ mẫu nào khác chưa từng có, bạn sẽ nhận được một ngoại lệ thời gian chạy, nhưng tôi thấy nó hữu ích hơn để xem dữ liệu bạn đang nhận được. Sau đó, nó thường đơn giản để xem lý do tại sao nó không phù hợp với các mẫu trước đó.

Tất nhiên bạn không muốn để điều này trong mã sản xuất. Tôi chỉ chèn dấu vết khi cần thiết rồi xóa chúng khi tôi đã hoàn thành. Bạn cũng có thể sử dụng CPP để loại bỏ chúng khỏi các bản dựng sản phẩm.

Tôi cũng muốn nói rằng tôi nghĩ rằng kết hợp mẫu là cách sai để thực hiện việc này. Bạn sẽ kết thúc với một vụ nổ tổ hợp trong số lượng các mẫu, mà nhanh chóng phát triển không thể quản lý. Nếu bạn muốn tạo ví dụ Float cho ví dụ Expr, bạn sẽ cần một số hàm tạo nguyên thủy hơn.

Thay vào đó, bạn có lẽ có chức năng thông dịch viên interpret :: Expr -> Double hoặc ít nhất có thể viết một.Sau đó, bạn có thể định nghĩa

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

Bằng mô hình kết hợp, bạn đang chủ yếu lại bằng văn bản giải thích chức năng của bạn trong Eq dụ. Nếu bạn muốn tạo một phiên bản Ord, bạn sẽ lại viết lại hàm giải thích.

+0

Dấu vết là con đường để đi. Đánh giá để tăng gấp đôi sẽ không hoạt động, bởi vì một số trường hợp của 'Expr' có thể là các hàm. –

2

Nếu bạn muốn nhận một số ví dụ về cách kết hợp có thể không thành công, bạn có thể xem QuickCheck. Có một ví dụ trên manual (kích thước của dữ liệu thử nghiệm) về việc tạo và thử nghiệm các kiểu dữ liệu đệ quy có vẻ hoàn toàn phù hợp với nhu cầu của bạn.

Trong khi cờ -Wall cung cấp cho bạn danh sách các mẫu không phù hợp, việc chạy QuickCheck cung cấp cho bạn ví dụ về dữ liệu đầu vào dẫn đến đề xuất đã cho của bạn bị lỗi. Ví dụ, nếu tôi viết một máy phát điện cho Expr và tôi đưa vào đầu vào quickCheck một đề xuất prop_expr_eq :: Expr -> Bool để kiểm tra nếu một số Expr bằng chính nó, tôi nhận được rất nhanh chóng Const 0.0 làm ví dụ đầu tiên về đầu vào không khớp.

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 

Như bạn thấy, chạy thử nghiệm cho bạn một ví dụ để chứng minh rằng kiểm tra bình đẳng của bạn là sai. Tôi biết điều này có thể là một chút quá mức cần thiết, nhưng lợi thế nếu bạn viết những điều tốt là bạn cũng có được các bài kiểm tra đơn vị cho mã của bạn nhìn vào các thuộc tính tùy ý, không chỉ mô hình phù hợp với tính đầy đủ.

*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: Đọc tốt về kiểm tra đơn vị với QuickCheck có trong sách miễn phí real world haskell.

1

Bạn có thể phá vỡ mẫu phức tạp của mình thành các mẫu đơn giản hơn và sử dụng trace để xem điều gì đang diễn ra. Một cái gì đó như thế này:

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 

Đó là một chút tuyệt vọng, nhưng thời gian tuyệt vọng kêu gọi các biện pháp tuyệt vọng. :) Khi bạn quen với Haskell bạn hiếm khi, nếu bao giờ, cần phải làm điều này.

Các vấn đề liên quan