2017-06-19 33 views
4

假設我有記錄定義什麼是創建記錄表單的最短途徑?

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 

什麼shortways創建從[String]記錄從getLine

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

讀這是我迄今爲止做的最好的。

{-# LANGUAGE NamedFieldPuns #-} 

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

我使用這個模式很多打codingame bot programmings時,這將是很好,如果有一個更好的方式來做到這一點。

回答

8

RecordWildCards刪除了一半的樣板。

{-# LANGUAGE RecordWildCards #-} 

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

你可以用SYB做到這一點,是這樣的:

{-# 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 

輸出:

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

你只需要讓你的類型數據(deriving Data)的一個實例。

0

就我個人而言,我會去RecordWildCards並稱它爲一天。但是,這是另一個有趣的方式,在某些情況下可能會很有用:謹慎風,使用動態類型來獲得改變類型的摺疊!

{-# 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 

這將啓動類型從Zone構造,:

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

然後相繼其應用於每個Int從輸入讀取,改變它的類型:

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

而且它的工作原理:

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

如果提供的參數太少,你會得到Nothing因爲運行時轉換失敗:

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

不過,如果你提供太多許多參數,你會得到一個異常:

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

這是很容易通過使用dynApply而不是dynApp來修復,其返回Maybe而不是拋出。而且只要你在Maybe正在工作,你還不如用Text.Read.readMaybe處理分析錯誤:

{-# 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 

真正雖然,可能不這樣做。

相關問題