2012-04-13 27 views
12

Hãy xem xét loại Ngày giờ trong đó ngày phải có, nhưng phần thời gian tính bằng giây là tùy chọn. Nếu phần thời gian ở đó, cũng có thể có một phần mili giây tùy chọn. Nếu có mili giây, có thể có một phần nano giây.Dữ liệu "Tùy chọn phụ thuộc" trong Haskell

Có nhiều cách để đối phó với điều này, ví dụ .:

--rely on smart constructors 
data DateTime = DateTime { days:: Int, 
          sec :: Maybe Int, 
          ms :: Maybe Int, 
          ns :: Maybe Int 
         } 

-- list all possibilities 
data DateTime = DateOnly Int 
       | DateWithSec Int Int 
       | DateWithMilliSec Int Int Int 
       | DateWithNanoSec Int Int Int Int  

-- cascaded Maybe 
data DateTime = DateTime Int (Maybe (Int, Maybe (Int, Maybe Int))) 

-- cascaded data 
data Nano = NoNano | Nano Int 
data MilliSec = NoMilliSec | MilliSec Int Nano 
data Sec = NoSec | Sec Int MilliSec 
data Date = Date Int Sec 

nào xây dựng bạn sẽ sử dụng (tất nhiên không giới hạn ở các ví dụ trên), và tại sao?

[Intentions]

tôi đang khám phá các khả năng cho một loại ngày trong Frege (http://code.google.com/p/frege/), sử dụng date4j của DateTime như một hướng dẫn dòng (như Haskell của ngày và thời gian lib là cách quá phức tạp, và java.util.Date quá bị hỏng). Trong thực hiện đồ chơi hiện tại của tôi tất cả các trường là bắt buộc, nhưng tất nhiên nó sẽ được tốt đẹp để giải phóng người dùng từ độ chính xác không mong muốn (và thực hiện ban đầu trường tùy chọn).

Vì vậy, mục tiêu chính là:

  • an toàn: bang bất hợp pháp phải tránh bằng mọi giá
  • tiện theo dõi: Nó phải là dễ dàng để làm việc với các loại, ví dụ khớp mẫu sẽ được mát mẻ, tính toán lịch nên dễ ...

Không phải như vậy quan trọng là:

  • hiệu: Tất nhiên làm việc với các loại không nên quá chậm, nhưng đối với các điển hình sử dụng nó không cần phải trải ra chu kỳ đồng hồ cuối cùng
  • bộ nhớ: Trong trường hợp điều này thực sự quan trọng, sẽ dễ dàng có được định dạng lưu trữ nhỏ gọn hơn
  • triển khai thực hiện: Đó là thư viện và tôi sẵn sàng thêm tất cả mã cần thiết để làm cho mọi thứ trơn tru

Điều đó nói rằng, tất cả điều này là rất dự kiến ​​và không nên quá nghiêm trọng.

+1

Không chắc chắn điều này có thể trả lời mà không biết thêm về yêu cầu của bạn. Nó có thể phụ thuộc vào nhiều thứ, bao gồm những chức năng mà bạn dự định xác định về loại, hiệu suất và cân nhắc về không gian (số lượng các indirections, cơ hội để giải nén các trường, v.v.) – hammar

+2

Một tùy chọn khác là sử dụng một hàm tạo đơn giản 'DateTime = DateTime [Int ] 'không được xuất từ ​​thư viện và buộc tất cả tương tác với kiểu phải thông qua các hàm như' mkDateTimeWithSec :: [Int] -> DateTime', 'isDateTimeWithSec :: DateTime -> Bool',' getSeconds :: DateTime -> Có thể Int' vv Bạn không thể sử dụng mẫu phù hợp (nhưng có thể không quá khủng khiếp vì bạn chỉ cần thay thế bằng bảo vệ và sử dụng chức năng truy cập) nhưng bạn không cho phép người dùng tạo trạng thái bất hợp pháp, vì bạn có thể kiểm tra mọi thứ tại thời điểm xây dựng (ví dụ: bạn có thể kiểm tra các giá trị âm). –

+1

Xác nhận ban đầu rằng dữ liệu không xác định là tùy chọn chứ không phải là mặc định làm cho một loại rất giả tạo - viết chức năng để thao tác nó sẽ là tẻ nhạt. Bạn cũng có thể có rác như một giá trị chỉ với nano giây nhưng không có gì giây. Tôi muốn thay đổi tack và làm việc cho dù mặc định 0 cho giá trị thời gian không xác định là bất lợi trong lý thuyết và thực hành. –

Trả lời

10

(Đây không phải là một câu trả lời, nhưng nó quá dài cho một lời nhận xét và sẽ được rõ ràng hơn ở đây.)

Có một cách khác mà bạn có thể xử lý nó: có một đơn DateTime loại lưu trữ tất cả các lĩnh vực luôn cùng với một tham số đại diện cho độ chính xác, ví dụ

data Precision = Days | Seconds | Milliseconds | Nanoseconds deriving (Ord, Eq {- etc -}) 
data DateTime = DateTime { prec :: Precision, 
          days :: Int, 
          sec :: Int, 
          ms :: Int, 
          ns :: Int } 

Và sử dụng các nhà thầu thông minh đặt thông số không sử dụng là 0. Nếu bạn có dateDifference hoặc bất cứ điều gì, bạn có thể truyền chính xác thông qua (ví dụ Ord sẽ làm cho gọn gàng này).

(Tôi đã có chút ý tưởng về làm thế nào tốt/Haskell-y này, nhưng các giải pháp khác có vẻ khá lộn xộn, có lẽ đây là tao nhã hơn.)

+1

Tôi sắp tự giới thiệu loại giải pháp này. – augustss

+1

Đây là loại điều tôi đã đề xuất, nhưng tôi cũng đã đề xuất chỉ thu gọn các trường 'sec',' ms' và 'ns' thành một trường' withinDay' đơn có cách diễn giải là giây, mili giây, hoặc nano giây tùy thuộc vào giá trị của trường 'prec'. –

+2

@DanielWagner, tôi không nghĩ rằng nó sẽ hoạt động, không phải thay đổi kiểu dữ liệu: (đối với nền tảng 32 bit) giá trị tối đa của 'Int' là vài tỷ, nhưng có 86 nghìn tỷ nano giây trong một ngày. – huon

8

“trạng thái bất hợp pháp phải tránh bằng mọi giá” và "mô hình phù hợp sẽ được mát mẻ" là nguyên tắc tốt mà trong trường hợp này là trong cuộc xung đột trực tiếp với nhau.

Ngoài ra, ngày tháng và thời gian là cấu trúc văn hóa con người gnarly với nhiều trường hợp cạnh và góc bất thường. Chúng không phải là loại quy tắc mà chúng ta có thể dễ dàng mã hóa trong hệ thống kiểu.

Vì vậy, trong trường hợp này, tôi sẽ đi với loại dữ liệu mờ, các nhà xây dựng thông minh và trình giải mã thông minh. Luôn luôn có các mẫu xem và bảo vệ mẫu cho các dịp khi chúng tôi muốn sử dụng đối sánh mẫu.

(Và tôi đã thậm chí không thảo luận dữ liệu tùy chọn phụ thuộc như một nhân tố thúc đẩy.)

+0

Xem các mẫu sẽ rất hữu ích ở đây, nhưng hiện không được hỗ trợ trong ngôn ngữ mục tiêu của tôi Frege (mà nếu không thì rất giống với Haskell) – Landei

+0

Thật không may. Tôi nghĩ rằng tôi sẽ đi theo cách này bất kể, và hy vọng Frege hỗ trợ các mô hình xem trong tương lai. – dave4420

3

Lấy cảm hứng từ giải pháp @ dbaupp của tôi muốn thêm một phiên bản loại phantom để các ứng viên:

-- using EmptyDataDecls 
data DayPrec 
data SecPrec 
data MilliPrec 
data NanoPrec 

data DateTime a = DateTime { days :: Int, sec :: Int, ms :: Int, ns :: Int } 

date :: Int -> DateTime DayPrec 
date d = DateTime d 0 0 0 

secDate :: Int -> Int -> DateTime SecPrec 
secDate d s = DateTime d s 0 0 

...  

--will work only for same precision which is a Good Thing (tm) 
instance Eq (DateTime a) where 
    (DateTime d s ms ns) == (DateTime d' s' ms' ns') = [d,s,ms,ns] == [d',s',ms',ns'] 

Nếu tôi không nhầm, điều này cho phép tôi làm việc với một loại, nhưng để phân biệt các biện pháp nếu tôi cần. Nhưng tôi đoán cũng sẽ có một số nhược điểm ...

+0

Tôi thích điều này, ngoại trừ tôi nghĩ rằng điều này sẽ làm cho một số điều hơi khó xử, ví dụ: tính toán thời gian giữa 'DateTime DatePrec' và' DateTime SecPrec' và nhận được độ chính xác chính xác (có lẽ là 'DayPrec'): Tôi nghĩ rằng nó sẽ yêu cầu một cái gì đó giống như phụ thuộc chức năng hoặc họ loại, hoặc gõ ra rất nhiều khai báo cá thể. (Giả sử rằng một hoạt động như vậy là mong muốn và được xác định rõ ràng.) – huon

+1

Tôi nghĩ rằng người dùng không nên trộn các phần khác nhau, nhưng thay vào đó một cách rõ ràng cắt ngắn hoặc mở rộng độ chính xác trước khi tính toán. – Landei

+0

Loại 'parseDateTime' sẽ là gì? (Nếu lập trình viên muốn duy trì mức độ chính xác trong chuỗi ban đầu, nhưng không biết tại thời gian biên dịch bao nhiêu độ chính xác để mong đợi?) – dave4420

0

Giả sử rằng bạn chỉ muốn giải quyết vấn đề này (trái ngược với một vấn đề chung) thì thư viện decimal có thể là những gì bạn muốn.

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