我是一个相当新的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
我错过了什么?
答案 0 :(得分:16)
最直接的方法是手工完成:
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
答案 1 :(得分:11)
有点丑陋的解决方案......: - o
让您的类型派生Read
:
data OrbitElements = OrbitElements { ... }
deriving (Read)
然后您可以通过
定义fromList
fromList :: [Double] -> OrbitElements
fromList ds = read $ "OrbitElement " ++ (concat $ Data.List.intersperse " " $ map show ds)
答案 2 :(得分:5)
你错过了Haskell静态类型的事实。不,Haskell没有任何这样的结构。
让我们假设语言有一些方法可以从列表中填充构造函数值。以下是一些需要考虑的问题:
答案 3 :(得分:2)
代数数据类型应该一次初始化,而不是一次初始化,就像你正在做的那样。正确的方法是:
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
定义了一个名为epoch
的函数,它将epoch
的定义掩盖为一个字段(你应该总是在启用警告的情况下进行编译,以便编译器能够捕获这样的东西),以及这个新函数取任何值o
(不仅仅是先前定义的OrbitElements
)并返回d !! 0
而不对o
做任何事情。如果您确实想要设置或更改epoch
的{{1}}字段,那么正确的方法是o
,它将返回一个新的let o' = o {epoch = d !! 0}
对象及其OrbitElements
1}}字段已更改。