9

我是一个相当新的 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
                                   }

我正在从一个文本文件中提取一些信息,这些信息最终出现在一个 Doubles 列表中。有没有一种简单的方法可以用列表初始化这种数据类型?我可以单独调用每个设置器,但是当我已经拥有列表中的所有值时,这似乎非常低效。

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

我错过了什么?

4

4 回答 4

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

现在我们可以fromList在 Applicative 样式中定义:

fromList' :: [Double] -> Maybe OrbitElements
fromList' = evalStateT $ OrbitElements
    <$> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*  endOfList
于 2013-03-31T21:30:42.380 回答
11

一个有点丑陋的解决方案...... :-o

让你的类型派生Read

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

然后你可以fromList定义

fromList :: [Double] -> OrbitElements
fromList ds = read $ "OrbitElement " ++ (concat $ Data.List.intersperse " " $ map show ds)
于 2013-03-31T23:22:33.663 回答
5

您错过了 Haskell 是静态类型的事实。不,Haskell 没有任何这样的结构。

让我们假设该语言有一些方法可以从列表中填充构造函数值。以下是一些需要思考的问题:

  • 如果列表包含的项目比要求的多或少,会发生什么?
  • 您将如何初始化其字段未统一键入的记录?
于 2013-03-31T21:26:40.300 回答
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字段o,正确的方法是let o' = o {epoch = d !! 0},它返回一个更改OrbitElements了其epoch字段的新对象。

于 2013-03-31T21:27:51.930 回答