2015-08-22 13 views
10

Tôi cóTôi có thể khớp với ký tự đại diện của nhà xây dựng dữ liệu trong Haskell không?

data Foo = X (String, Int) | A String | B String | C String | D String -- ... 

và đã xác định

f (X (s, _)) =s 
f (A s) = s 
f (B s) = s 
f (C s) = s 
f (D s) = s 
-- ... 

nhưng muốn để có thể viết một cái gì đó giống như

f (X (s, _)) =s 
f (_ s) = s 

Nhưng có vẻ là không có cách nào để làm điều này (tôi nhận được "lỗi phân tích cú pháp" được liên kết với số _).

Có cách nào để khớp với ký tự đại diện của nhà xây dựng dữ liệu trong Haskell không?

+0

Khi không có ngôn ngữ nào có sẵn trong ngôn ngữ (ngoại trừ, bạn có thể cân nhắc sử dụng [generics] (https://wiki.haskell.org/Generics) hoặc [Template Haskell] (https: //wiki.haskell. org/Template_Haskell) để tạo mã như vậy, nó có hợp lý cho trường hợp sử dụng của bạn không? –

Trả lời

15

Không. Nhưng bạn có thể viết điều này:

data Foo 
    = X { name :: String, age :: Int } 
    | A { name :: String } 
    | B { name :: String } 
    | C { name :: String } 
    | D { name :: String } 

và sau đó có name :: Foo -> String. Bạn cũng có thể xem xét điều này:

data Tag = A | B | C | D 
data Foo = X String Int | Tagged Tag String 

f (X s _) = s 
f (Tagged _ s) = s 
+0

Tôi đoán lựa chọn phương pháp tiếp cận (phương pháp tôi sử dụng so với phương pháp thứ hai, ít nhất) phụ thuộc vào số lượng "được gắn thẻ" Trong thực tế, tôi chỉ có một vài (và có nhiều nhà xây dựng với các hình thức hơi khác nhau) và tôi nghĩ rằng cách tiếp cận tôi có thể sạch sẽ nhất. Có lý do * khái niệm nào để chọn một trong ba cách tiếp cận này so với cách tiếp cận khác không? – orome

+0

@ raxacoricofallapatorius Cái nào có ý nghĩa phụ thuộc rất lớn vào những gì bạn muốn loại có ý nghĩa - giống như bạn có thể viết '2 * 1 + 2 * 2 + 2 * 3' trong một số trường hợp và' 2 * (1 + 2 + 3) ' ở những người khác để nhấn mạnh các lý do khác nhau vì đã quan tâm đến số '12'. –

+1

@raxacoricofallapatorius, nếu các chuỗi đại diện cho cùng một điều trong mỗi trường hợp, cách tiếp cận thẻ có ý nghĩa. Nếu các chuỗi không liên quan, bạn có thể muốn có ba hàm tạo. Thực tế là bạn muốn làm điều tương tự với các chuỗi bất kể gợi ý, nhưng không ngụ ý, rằng cách tiếp cận được gắn thẻ có ý nghĩa. – dfeuer

3

Ngoài câu trả lời của @ DanielWagner, cách khác là sử dụng Scrap your boilerplate (AKA "SYB"). Nó cho phép bạn tìm thấy subterm đầu tiên của một loại nhất định. Vì vậy, bạn có thể xác định

{-# LANGUAGE DeriveDataTypeable #-} 

import Control.Monad 
import Data.Data 
import Data.Generics.Schemes 
import Data.Typeable 

data Foo = X (String, Int) | A String | B String | C String | D String 
    deriving (Show, Eq, Ord, Data, Typeable) 

fooString :: Foo -> Maybe String 
fooString = msum . gmapQ cast 

fooString sẽ trả lại đối số đầu tiên String của các nhà thầu của bạn. Chức năng cast lọc ra String s và gmapQ nhận các giá trị được lọc cho tất cả các lớp con ngay lập tức.

Tuy nhiên, điều này sẽ không trả lại String từ X, vì X không có ngay lập tức String subterm, nó chỉ có một subterm loại (String, Int). Để có được String đầu tiên bất cứ nơi nào trong hệ thống phân cấp hạn, bạn có thể sử dụng everywhere:

fooString' :: Foo -> Maybe String 
fooString' = everything mplus cast 

Lưu ý rằng phương pháp này là hơi mong manh: Nó bao gồm đơn giản là tất cả String là nó tìm thấy, trong đó có thể không phải luôn luôn những gì bạn muốn , đặc biệt, nếu sau này bạn mở rộng kiểu dữ liệu của mình (hoặc một số kiểu dữ liệu mà nó tham chiếu).

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