2013-03-31 38 views
9

Tôi là một lập trình viên Haskell khá mới và tôi đang cố gắng tìm ra cách để có được một số giá trị thành một kiểu dữ liệu đại số.Khởi tạo kiểu dữ liệu đại số từ danh sách

Tôi có một loại bản ghi dữ liệu:

data OrbitElements = OrbitElements { epoch :: Double, 
            ecc :: Double, 
            distPeri :: Double, 
            incl :: Double, 
            longAscNode :: Double, 
            argPeri :: Double, 
            timePeri :: Double, 
            meanMotion :: Double, 
            meanAnomaly :: Double, 
            trueAnomaly :: Double, 
            semiMajorAxis :: Double, 
            distApo :: Double, 
            period :: Double 
            } 

tôi kéo vào một số thông tin từ một tập tin văn bản, mà kết thúc trong một danh sách các đôi. Có cách nào dễ dàng để khởi tạo kiểu dữ liệu này với danh sách không? Tôi chỉ có thể gọi từng setter riêng lẻ nhưng điều đó có vẻ không hiệu quả khủng khiếp khi tôi đã có tất cả các giá trị trong một danh sách.

let d = [2456382.5,6.786842103348031e-3,0.7184187640759256,3.394660181513041,76.64395338801751,55.2296201483587,2456457.141012543,1.602144936476915,240.4142797010899,239.7408018186761,0.7233278761603762,0.7282369882448266,224.6987721295883] 
let o = OrbitElements 
let epoch o = d !! 0 
let ecc o = d !! 1 
-- and so on 

Tôi đang thiếu gì?

Trả lời

16

Cách trực tiếp nhất là để chỉ làm điều đó bằng tay:

fromList :: [Double] -> Maybe OrbitElements 
fromList [ _epoch 
     , _ecc 
     , _distPeri 
     , _incl 
     , _longAscNode 
     , _argPeri 
     , _timePeri 
     , _meanMotion 
     , _meanAnomaly 
     , _trueAnomaly 
     , _semiMajorAxis 
     , _distApo 
     , _period 
     ] 
    = Just $ OrbitElements 
      _epoch 
      _ecc 
      _distPeri 
      _incl 
      _longAscNode 
      _argPeri 
      _timePeri 
      _meanMotion 
      _meanAnomaly 
      _trueAnomaly 
      _semiMajorAxis 
      _distApo 
      _period 
fromList _ = Nothing 

Tuy nhiên, có một cách nhẹ quyến rũ, mà là để phân tích chúng phần tử bằng yếu tố, đó là ít dễ bị lỗi và mô tả nhiều hơn Đầu tiên chúng tôi định nghĩa hai trình phân tích cú pháp, một trong số đó yêu cầu một phần tử mới từ danh sách (hoặc không thành công nếu danh sách trống), và thứ hai trong số đó khớp với phần cuối của danh sách (hoặc không thành công nếu danh sách không trống):

import Control.Applicative 
import Control.Monad 
import Control.Monad.Trans.State 

getElem :: StateT [Double] Maybe Double 
getElem = do 
    s <- get 
    case s of 
     [] -> mzero 
     x:xs -> do 
      put xs 
      return x 

endOfList :: StateT [Double] Maybe() 
endOfList = do 
    s <- get 
    case s of 
     [] -> return() 
     _ -> mzero 

Bây giờ chúng ta có thể xác định fromList trong phong cách applicative:

fromList' :: [Double] -> Maybe OrbitElements 
fromList' = evalStateT $ OrbitElements 
    <$> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <*> getElem 
    <* endOfList 
+1

Thanks, điều này khẳng định nghi ngờ của tôi, và bất kỳ câu trả lời mà bắt đầu ra 'Trước hết, chúng ta định nghĩa hai trình phân tích cú pháp ... "rất tuyệt trong sách của tôi. :) –

+4

Tùy chọn" bằng tay "trở thành một trình cắt nhỏ hơn khi được kết hợp với [' -XRecordWildCards'] (http://www.haskell.org/ghc/docs/7.4 .2/html/users_guide/syntax-extns.html # record-wildcards): 'fromList [epoch, ecc, distPeri, incl, longAscNode, argPeri, timePeri, meanMotion, meanAnomaly, trueAnomaly, semiMajorAxis, distApo, period] = Chỉ OrbitElements {..} '. –

5

Bạn đang thiếu thực tế là Haskell được nhập tĩnh. Không, Haskell không có bất kỳ cấu trúc nào như vậy.

Giả sử ngôn ngữ có cách nào đó để điền vào các giá trị hàm tạo từ danh sách. Dưới đây là một số câu hỏi để suy nghĩ về:

  • Điều gì sẽ xảy ra khi danh sách chứa nhiều hoặc ít mục hơn yêu cầu?
  • Làm cách nào để bạn khởi tạo một bản ghi có các trường không được nhập thống nhất?
2

Loại dữ liệu đại số có nghĩa là được khởi tạo cùng một lúc, không phải là trường tại một thời điểm bạn đang làm. Cách chính xác để thực hiện việc này là:

let d = ... 
let o = OrbitElements {epoch = d !! 0 
         ecc = d !! 1, 
         distPeri = d !! 2, 
         incl = d !! 3, 
         longAscNode = d !! 4, 
         argPeri = d !! 5, 
         timePeri = d !! 6, 
         meanMotion = d !! 7, 
         meanAnomaly = d !! 8, 
         trueAnomaly = d !! 9, 
         semiMajorAxis = d !! 10, 
         distApo = d !! 11, 
         period = d !! 12} 

Lưu ý rằng cách bạn đang thực hiện không thực sự đặt bất kỳ giá trị nào trong o. let epoch o = d !! 0 xác định hàm có tên epoch để che dấu định nghĩa epoch làm trường (Bạn nên luôn biên dịch với cảnh báo được bật để trình biên dịch sẽ bắt được những thứ như thế này) và hàm mới này có giá trị o (không chỉ OrbitElements được xác định trước đó) và trả lại d !! 0 mà không cần làm bất cứ điều gì với o. Nếu bạn thực sự muốn đặt hoặc thay đổi trường epoch của o, cách chính xác để thực hiện là let o' = o {epoch = d !! 0}, trả về đối tượng OrbitElements mới với trường epoch đã thay đổi.

11

Một giải pháp somwhat xấu xí ...: -o

Hãy loại bạn lấy Read:

data OrbitElements = OrbitElements { ... } 
         deriving (Read) 

Sau đó, bạn có thể định nghĩa fromList bởi

fromList :: [Double] -> OrbitElements 
fromList ds = read $ "OrbitElement " ++ (concat $ Data.List.intersperse " " $ map show ds) 
+1

Điều này thực sự thông minh! –

+2

Một số ghi chú kiểu ngớ ngẩn: 'concat' với' intersperse' giống với 'intercalate'. 'intercalate" "' giống như 'unwords'. Vì vậy, bạn có thể viết lại phần thứ hai của định nghĩa của bạn là 'unwords $ map show ds'. –

+1

Vì vậy, 'fromList ds = read $" OrbitElement "++ (unwords $ map show ds)'. Có lẽ, người ta cần phải viết 'Data.List.unwords'. – md2perpe

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