2017-06-19 6 views
4

Angenommen, ich habe Rekord DefinitionWas ist der kürzeste Weg zum Erstellen einer Datensatzliste?

data Zone = Zone 
    { zId  :: Int -- this zone's ID 
    , zOwnerId :: Int -- the player who owns this zone (-1 otherwise) 
    , zPodsP0 :: Int -- player 0's PODs on this zone 
    , zPodsP1 :: Int -- player 1's PODs on this zone 
    , zPodsP2 :: Int -- player 2's PODs on this zone (always 0 for a two player game) 
    , zPodsP3 :: Int -- player 3's PODs on this zone (always 0 for a two or three player game) 
    } deriving Show 

Was der shortways ist Rekord von [String] von getLine

zones <- replicateM zoneCount $ fmap (mkZone . words) getLine 

lesen zu erstellen Dies ist das Beste, was ich bisher tun.

{-# LANGUAGE NamedFieldPuns #-} 

mkZone :: [String] -> Zone 
mkZone xs = Zone {zId, zOwnerId, zPodsP0, zPodsP1, zPodsP2, zPodsP3} 
    where [zId, zOwnerId, zPodsP0, zPodsP1, zPodsP2, zPodsP3] = map read xs 

Ich benutze dieses Muster eine Menge, wenn codingame spielt bot programmings, wäre es schön, wenn es ein besserer Weg, dies zu tun.

Antwort

8

RecordWildCards entfernt die Hälfte Ihres Boilerplate.

{-# LANGUAGE RecordWildCards #-} 

mkZone :: [String] -> Zone 
mkZone xs = Zone {..} 
    where [zId, zOwnerId, zPodsP0, zPodsP1, zPodsP2, zPodsP3] = map read xs 
1

du mit SYB tun, wie folgt aus:

{-# LANGUAGE DeriveDataTypeable #-} 
{-# LANGUAGE ScopedTypeVariables #-} 

import Data.Data 
import Control.Monad.State 

data Zone = Zone { zId, zOwnerId, zPodsP0, zPodsP1, zPodsP2, zPodsP3 :: Int } 
    deriving (Show, Data) 

main = do 
    print (mygread ["1", "2", "3", "4", "5", "6"] :: Maybe Zone) 
    print (mygread ["a", "2", "3", "4", "5", "6"] :: Maybe Zone) 
    print (mygread ["1", "2", "3", "4", "5"] :: Maybe Zone) 

mygread :: forall a . Data a => [String] -> Maybe a 
mygread = evalStateT (fromConstrM read' constr) 
    where 
    constr = head . dataTypeConstrs . dataTypeOf $ (undefined :: a) 
    read' :: forall a . Data a => StateT [String] Maybe a 
    read' = do 
     x:xs <- get 
     put xs 
     lift . fmap fromConstr . readConstr (dataTypeOf (undefined :: a)) $ x 

Ausgang:

Just (Zone {zId = 1, zOwnerId = 2, zPodsP0 = 3, zPodsP1 = 4, zPodsP2 = 5, zPodsP3 = 6}) 
Nothing 
Nothing 

Sie müssen nur Ihre Art eine Instanz von Daten (deriving Data) machen.

0

Persönlich würde ich für RecordWildCards gehen und es einen Tag nennen. Aber hier ist eine weitere hackische, aber interessante Möglichkeit, dies zu tun, die unter bestimmten Umständen nützlich sein kann: Vorsicht in den Wind legen und dynamische Typen verwenden, um eine typverändernde Falte zu erhalten!

{-# LANGUAGE DeriveDataTypeable #-} 

import Data.Dynamic (dynApp, fromDynamic, toDyn) 
import Data.List (foldl') 
import Data.Typeable (Typeable) 

-- Add the 'Typeable' instance to enable runtime type information. 
data Zone = Zone 
    { zId, zOwnerId, zPodsP0, zPodsP1, zPodsP2, zPodsP3 :: Int 
    } deriving (Show, Typeable) 

mkZone :: [String] -> Maybe Zone 
mkZone = fromDynamic . foldl' dynApp (toDyn Zone) . map (toDyn . readInt) 
    where 

    -- This type-specialised 'read' avoids an ambiguous type. 
    readInt :: String -> Int 
    readInt = read 

Dies beginnt mit dem Zone Konstruktor vom Typ:

Int -> Int -> Int -> Int -> Int -> Int -> Zone 

es dann jedem von dem Eingangs lesen Int nacheinander gilt, seine Art zu ändern:

Int -> Int -> Int -> Int -> Int -> Zone 
Int -> Int -> Int -> Int -> Zone 
Int -> Int -> Int -> Zone 
Int -> Int -> Zone 
Int -> Zone 
Zone 

Und es funktioniert :

> mkZone ["1", "2", "3", "4", "5", "6"] 
Just (Zone {zId = 1, zOwnerId = 2, zPodsP0 = 3, zPodsP1 = 4, zPodsP2 = 5, zPodsP3 = 6}) 

Wenn Sie zu wenige Argumente liefern, Sie Nothing bekommen, weil die Laufzeit Umwandlung fehlschlägt:

> mkZone ["1", "2", "3", "4", "5"] 
Nothing 

Wenn Sie jedoch zu viele Argumente liefern, erhalten Sie eine Ausnahme:

> mkZone ["1", "2", "3", "4", "5", "6", "7"] 
*** Exception: Type error in dynamic application. 
Can't apply function <<Zone>> to argument <<Int>> 

, die leicht ist behoben mit dynApply anstelle von dynApp, die eine Maybe anstelle von werfen zurückgibt. Und solange Sie in Maybe arbeiten, könnten Sie auch Text.Read.readMaybe verwenden, um handhaben Fehler analysieren:

{-# LANGUAGE DeriveDataTypeable #-} 

import Control.Monad ((<=<)) 
import Data.Dynamic (Dynamic, dynApply, fromDynamic, toDyn) 
import Data.List (foldl') 
import Data.Typeable (Typeable) 
import Text.Read (readMaybe) 

data Zone = Zone { … } deriving (Show, Typeable) 

mkZone :: [String] -> Maybe Zone 
mkZone = fromDynamic <=< foldl' go (Just (toDyn Zone)) . map readInt 
    where 

    go :: Maybe Dynamic -> Maybe Int -> Maybe Dynamic 
    go mAcc mx = do 
     acc <- mAcc 
     x <- mx 
     dynApply acc $ toDyn x 

    readInt :: String -> Maybe Int 
    readInt = readMaybe 

Für echte aber wahrscheinlich dies nicht tun.

Verwandte Themen