[英]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
在所有。 如果你真的想設置或改變o
的epoch
字段,那么正確的方法就是let o' = o {epoch = d !! 0}
let o' = o {epoch = d !! 0}
,返回一個新的OrbitElements
對象,其epoch
字段已更改。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.