【问题标题】:What is the shortest way to create a record form list?创建记录表单列表的最短方法是什么?
【发布时间】:2017-06-19 12:23:46
【问题描述】:

假设我有记录定义

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

[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

我在玩codingamebot programmings时经常使用这种模式,如果有更好的方法来做到这一点,那就太好了。

【问题讨论】:

    标签: haskell record


    【解决方案1】:

    RecordWildCards 删除了你的样板文件的一半。

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

    【讨论】:

      【解决方案2】:

      您可以使用 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
      

      您只需将您的类型设为 Data 的实例 (deriving Data)。

      【讨论】:

        【解决方案3】:

        就我个人而言,我会选择 RecordWildCards 并收工。但这是另一种 hackish 但有趣的方法,它在某些情况下可能有用:抛开谨慎,使用动态类型来获得类型更改折叠!

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

        但实际上,可能不要这样做。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-22
          • 1970-01-01
          • 1970-01-01
          • 2012-08-22
          • 2021-11-24
          • 1970-01-01
          • 2021-10-19
          相关资源
          最近更新 更多