2013-03-31 11 views
9

Jestem całkiem nowym programistą Haskella i próbuję wymyślić, jak wprowadzić pewne wartości do algebraicznego typu danych.Inicjowanie algebraicznego typu danych z listy

mam typ rekordu danych:

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 
            } 

ja ciągnąc w jakiejś informacji z pliku tekstowego, który kończy się na liście Doubles. Czy istnieje prosty sposób na zainicjowanie tego typu danych za pomocą listy? Mogę po prostu zadzwonić do każdego setera indywidualnie ale to wydaje się strasznie nieefektywne, gdy już mam wszystkie wartości na liście.

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 

Czego mi brakuje?

Odpowiedz

16

Najbardziej bezpośrednim sposobem jest po prostu to zrobić ręcznie:

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 

Nie ma jednak nieco bardziej seksownego sposób, który jest do analizowania ich element po elemencie, który jest mniej podatny na błędy i bardziej opisowe tego, co próbujemy zrobić:

Najpierw definiujemy dwa parsery, z których jeden żąda nowego elementu z listy (lub kończy się niepowodzeniem, jeśli lista jest pusta), a druga z nich pasuje do końca listy (lub zawiedzie, jeśli lista nie jest pusta):

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 

Teraz możemy zdefiniować fromList w stylu aplikacyjnych:

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

Dzięki, ten potwierdził moje podejrzenia, a każda odpowiedź, że zaczyna się „Najpierw definiujemy dwa parsery ... "jest świetny w mojej książce :) –

+4

Opcja" odręcznie "staje się przyjemniejsza w połączeniu z [' -XRecordWildCards'] (http://www.haskell.org/ghc/docs/7.4 .2/html/users_guide/syntax-extns.html # record-wildcards): 'fromList [epoka, ecc, distPeri, incl, longAscNode, argPeri, timePeri, meanMotion, meanAnomaly, trueAnomaly, semiMajorAxis, distApo, period] = Just OrbitElements {..} '. –

5

Brakuje ci tego, że Haskell jest statycznie wpisany. Nie, Haskell nie ma takiej konstrukcji.

Załóżmy, że język miał jakiś sposób, aby wypełnić wartości konstruktorów z listy. Oto kilka pytań do przemyślenia:

  • Co by się stało, gdyby lista zawierała więcej lub mniej elementów niż wymagane?
  • Jak zainicjowałbyś rekord, którego pola nie były równomiernie wpisane?
2

Algebraiczne typy danych mają być inicjowane wszystkie naraz, a nie pola w czasie, jak robisz. Poprawny sposób to zrobić:

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} 

Należy zauważyć, że sposób, w jaki robią to w rzeczywistości nie jest ustawienie dowolnych wartości w o. let epoch o = d !! 0 definiuje funkcję o nazwie epoch, która maskuje definicję epoch jako pola (Powinieneś zawsze kompilować z włączonymi ostrzeżeniami, aby kompilator złapał takie rzeczy), a ta nowa funkcja przyjmuje dowolną wartość o (nie tylko zdefiniowaną wcześniej OrbitElements) i zwraca d !! 0 w ogóle nie robiąc nic z o. Jeśli rzeczywiście chcesz ustawić lub zmienić pole epoch z o, prawidłowym sposobem wykonania tego będzie let o' = o {epoch = d !! 0}, który zwróci nowy obiekt ze zmienionym polem epoch.

11

somwhat brzydka rozwiązaniem ...: -o

Bądź typ czerpać Read:

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

Następnie można zdefiniować fromList przez

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

To jest naprawdę sprytne! –

+2

Kilka głupich notatek w stylu: 'concat' z' intersperse' jest tym samym, co 'intercalate'. 'intercalate" "' jest takie samo jak 'unwords'. Tak więc można przepisać drugą część definicji jako 'powizania $ map show ds'. –

+1

Zatem 'fromList ds = read $" OrbitElement "++ (unwords $ map show ds)'. Być może trzeba napisać 'Data.List.unwords'. – md2perpe

Powiązane problemy