从列表初始化代数数据类型

Jam*_*rbo 9 haskell list algebraic-data-types

我是一个相当新的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
                                   }
Run Code Online (Sandbox Code Playgroud)

我从一个文本文件中提取了一些信息,最终列在双打列表中.有没有一种简单的方法来使用列表初始化此数据类型?我可以单独调用每个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
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

Gab*_*lez 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
Run Code Online (Sandbox Code Playgroud)

然而,有一种稍微更性感的方式,即逐个元素地解析它们,这不容易出错并且更能描述我们想要做的事情:

首先我们定义两个解析器,其中一个解析器从列表中请求一个新元素(如果列表为空则失败),第二个解析器匹配列表的末尾(如果列表不为空则失败):

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
Run Code Online (Sandbox Code Playgroud)

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

fromList' :: [Double] -> Maybe OrbitElements
fromList' = evalStateT $ OrbitElements
    <$> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*  endOfList
Run Code Online (Sandbox Code Playgroud)

  • 与[-XRecordWildCards`]结合使用时,"手动"选项变得更加轻松(http://www.haskell.org/ghc/docs/7.4.2/html/users_guide/syntax-extns.html#record-通配符):`fromList [epoch,ecc,distPeri,incl,longAscNode,argPeri,timePeri,meanMotion,meanAnomaly,trueAnomaly,semiMajorAxis,distApo,period] = Just OrbitElements {..}`. (4认同)

md2*_*rpe 11

一个难看的解决方案......: - o

让你的类型派生Read:

data OrbitElements = OrbitElements { ... }
                         deriving (Read)
Run Code Online (Sandbox Code Playgroud)

然后,你可以定义fromList通过

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

  • 一些愚蠢的风格笔记:`concat`与`intersperse`与`intercalate`相同.`intercalate""`与`unwords`相同.因此,您可以将定义的第二部分重写为"unwords $ map show ds`". (2认同)

Nik*_*kov 5

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

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

  • 如果列表包含的项目多于或少于要求,会发生什么?
  • 如何初始化其字段未统一输入的记录?