2013-07-15 25 views
10

Trên http://lisperati.com/haskell/ht4.html tác giả hiển thị các hàm đọc đa giác từ một tệp SVG đơn giản. Tôi hiểu hầu hết mã, tuy nhiên tôi tự hỏi liệu có thể viết lại hàmLàm cách nào để các nhân viên bảo vệ chức năng của Haskell hoạt động trên các giá trị khác so với các thông số chức năng?

dưới dạng dễ hiểu hơn. Tôi thấy rằng dòng một chút bối rối kể từ khi bảo vệ nên hoạt động trên các thông số của hàm (trong trường hợp này là "readPoint") nhưng ở đây bảo vệ dường như hoạt động trên kết quả của matchRegex.

Vì vậy, bất kỳ ai cũng có thể giải thích sự kỳ diệu đằng sau điều này?

Và điều này có thể được viết lại thành một hình thức dễ hiểu hơn không?

Trả lời

9

Bạn có thể nghĩ đến bảo vệ như đường chỉ cú pháp cho một câu lệnh if. Biểu thức trong bộ bảo vệ có thể là bất kỳ biểu thức boolean hợp lệ nào, giống như trong câu lệnh if. Điều này có nghĩa là bạn có thể sử dụng bất kỳ giá trị và hàm nào trong phạm vi.

Ví dụ, bạn có thể viết lại như sau:

foo x | abc = ... 
     | def = ... 
     | otherwise = ... 

như

foo x = if abc then ... else if def then ... else ... 

Chúng tôi cũng có thể viết này một chút hơn một cách chi tiết với case hơn if:

foo x = case abc of 
    True -> ... 
    False -> case def of 
    True -> ... 
    False -> ... 

Xét cho cùng, if chính nó chỉ là cú pháp ar cho một trường hợp! Viết tất cả mọi thứ theo điều khoản của case làm cho nó dễ dàng hơn để xem làm thế nào các tính năng khác nhau chỉ là cú pháp đường cho cùng một điều.

Biểu thức thứ hai có ý nghĩa rõ ràng mặc dù các điều kiện tham chiếu các biến hiện tại (abcdef) thay vì tham số chức năng x; các vệ sĩ chỉ làm việc theo cùng một cách.

Ví dụ của bạn phức tạp hơn một chút vì nó sử dụng phần mở rộng có tên là "pattern guards". Điều này có nghĩa rằng người bảo vệ có thể nhiều hơn chỉ là một boolean - nó cũng có thể cố gắng để phù hợp với một mô hình. Nếu mẫu khớp với nhau, người bảo vệ thành công (ví dụ như người bảo vệ với True); nếu không, người bảo vệ sẽ không khớp (giống như nhận được False).

Chúng ta có thể tưởng tượng viết lại nó như sau:

readPoint s | Just [x, y] <- matchRegex (mkRegex "...") s = ... 
      | otherwise = ... 

như

readPoint s = case matchRegex (mkRegex "...") s of 
       Just [x, y] -> ... 
       _ -> ... 

Bạn có thể xem song song giữa điều này và phiên bản case của lính bình thường. Bảo vệ mô hình chỉ mở rộng các desugaring để tùy ý các mẫu thay vì chỉ booleans.

Một lần nữa, tôi chắc chắn bạn sẽ đồng ý rằng việc cho phép bất kỳ biểu thức nào trong tuyên bố case - không có lý do gì để hạn chế việc sử dụng đối số của hàm. Điều này cũng đúng đối với các vệ sĩ bởi vì chúng thực sự chỉ là đường cú pháp.

+3

Tuyệt vời! Nó luôn luôn tốt để nhắc nhở rằng khá nhiều tất cả mọi thứ trong Haskell chỉ là cú pháp đường cho một số trường hợp và biểu thức lambda. – leftaroundabout

5

Điều này được gọi là bảo vệ mẫu - đọc về nó here.

Đó là tương đương với

readPoint :: String -> Point 
readPoint s = case matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s of 
        Just [x,y] -> (read x,read y) 

Ý tưởng là bạn có thể thoát khỏi khó chịu phát biểu trường hợp lồng nhau. Sử dụng mô hình bảo vệ bạn có thể làm một cái gì đó giống như

readPoint :: String -> Point 
readPoint s | Just [x,y] <- matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s = (read x,read y) 
      | Just [x] <- matchRegex (mkRegex "([0-9.]+)") s = (read x,0) 
      | otherwise = (0,0) 

để thay thế cho nhiều tiết

readPoint :: String -> Point 
readPoint s = case matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s of 
        Just [x,y] -> (read x,read y) 
        Nothing -> case matchRegex (mkRegex "([0-9.]+)") s of 
            Just [x] -> (read x,0) 
             Nothing -> (0,0) 
Các vấn đề liên quan