簡體   English   中英

從列表初始化代數數據類型

[英]Initializing algebraic data type from list

我是一個相當新的Haskell程序員,我試圖弄清楚如何將一些值轉換為代數數據類型。

我有一個記錄數據類型:

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
                                   }

我從一個文本文件中提取了一些信息,最終列在雙打列表中。 有沒有一種簡單的方法來使用列表初始化此數據類型? 我可以單獨調用每個setter,但是當我已經擁有列表中的所有值時,這似乎非常低效。

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

我錯過了什么?

最直接的方法是手工完成:

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

然而,有一種稍微更性感的方式,即逐個元素地解析它們,這不容易出錯並且更能描述我們想要做的事情:

首先我們定義兩個解析器,其中一個解析器從列表中請求一個新元素(如果列表為空則失敗),第二個解析器匹配列表的末尾(如果列表不為空則失敗):

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

現在我們可以在Applicative樣式中定義fromList

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

一個難看的解決方案......: - o

讓你的類型派生Read

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

然后你可以定義fromList by

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

你錯過了Haskell靜態類型的事實。 不,Haskell沒有任何這樣的結構。

讓我們假設語言有一些方法可以從列表中填充構造函數值。 以下是一些需要考慮的問題:

  • 如果列表包含的項目多於或少於要求,會發生什么?
  • 如何初始化其字段未統一輸入的記錄?

代數數據類型意味着一次初始化,而不是一次初始化,就像你正在做的那樣。 正確的方法是:

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}

請注意,您執行此操作的方式實際上並未在o設置任何值。 let epoch o = d !! 0 let epoch o = d !! 0定義一個函數命名的epoch掩蓋的定義epoch作為一個字段(你應該總是啟用,這樣編譯器會趕上這樣的事情警告編譯),而這個新的功能需要的任何值o (不只是OrbitElements先前定義)並返回d !! 0 d !! 0沒有做任何事情o在所有。 如果你真的想設置或改變oepoch字段,那么正確的方法就是let o' = o {epoch = d !! 0} let o' = o {epoch = d !! 0} ,返回一個新的OrbitElements對象,其epoch字段已更改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM