创建记录表单列表的最短方法是什么?

时间:2017-06-19 12:23:46

标签: haskell record

假设我有记录定义

Insert INTO tableName select t1.student_Id, t1.student_name,1 from tablename t1 
where t1.version = 0 and not exists 
(select 1 from tablename  t2 where t2.student_id = t.student_id and t2.version = 1);

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

在播放编码bot programmings时我经常使用这种模式,如果有更好的方法可以做到这一点会很好。

3 个答案:

答案 0 :(得分:8)

RecordWildCards删除了一半的样板。

{-# LANGUAGE RecordWildCards #-}

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

答案 1 :(得分:1)

You can do this with SYB, like this:

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

Output:

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

You only need to make your type an instance of Data (deriving Data).

答案 2 :(得分: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

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