gpt4 book ai didi

Haskell根据字段名称字符串动态设置记录字段?

转载 作者:行者123 更新时间:2023-12-04 03:30:08 25 4
gpt4 key购买 nike

假设我有以下记录:

data Rec = Rec {
field1 :: Int,
field2 :: Int
}

如何编写函数:
changeField :: Rec -> String -> Int -> Rec
changeField rec fieldName value

这样我就可以将字符串“field1”或“field2”传入 fieldName参数并让它更新关联的字段?我了解 Data.DataData.Typeable是在这里使用什么,但我无法弄清楚这两个包。

我见过的一个库的例子是 cmdArgs。以下是 excerpt来自有关如何使用此库的博客文章:
{-# LANGUAGE DeriveDataTypeable #-}
import System.Console.CmdArgs

data Guess = Guess {min :: Int, max :: Int, limit :: Maybe Int} deriving (Data,Typeable,Show)

main = do
x <- cmdArgs $ Guess 1 100 Nothing
print x

现在我们有了一个简单的命令行解析器。一些示例交互是:
$ guess --min=10
NumberGuess {min = 10, max = 100, limit = Nothing}

最佳答案

好的,这是一个不使用模板haskell的解决方案,或者需要您手动管理字段映射。

我实现了一个更通用的modifyField它接受一个 mutator 函数,并实现了 setField (nee changeField )将它与 const value 一起使用.
modifyField的签名和 setField在记录和修改器/值类型中都是通用的;但是,为了避免 Num歧义,调用示例中的数字常量必须明确给出 :: Int签名。

我还更改了参数顺序,所以 rec最后出现,允许 modifyField 的链/setField由普通函数组合创建(参见最后一个调用示例)。
modifyField建立在基元 gmapTi 之上,这是 Data.Data 中的“缺失”函数.它是 gmapT 之间的交叉。和 gmapQi .

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}

import Data.Typeable (Typeable, typeOf)
import Data.Data (Data, gfoldl, gmapQi, ConstrRep(AlgConstr),
toConstr, constrRep, constrFields)
import Data.Generics (extT, extQ)
import Data.List (elemIndex)
import Control.Arrow ((&&&))

data Rec = Rec {
field1 :: Int,
field2 :: String
} deriving(Show, Data, Typeable)

main = do
let r = Rec { field1 = 1, field2 = "hello" }
print r
let r' = setField "field1" (10 :: Int) r
print r'
let r'' = setField "field2" "world" r'
print r''
print . modifyField "field1" (succ :: Int -> Int) . setField "field2" "there" $ r
print (getField "field2" r' :: String)

---------------------------------------------------------------------------------------

data Ti a = Ti Int a

gmapTi :: Data a => Int -> (forall b. Data b => b -> b) -> a -> a
gmapTi i f x = case gfoldl k z x of { Ti _ a -> a }
where
k :: Data d => Ti (d->b) -> d -> Ti b
k (Ti i' c) a = Ti (i'+1) (if i==i' then c (f a) else c a)
z :: g -> Ti g
z = Ti 0

---------------------------------------------------------------------------------------

fieldNames :: (Data r) => r -> [String]
fieldNames rec =
case (constrRep &&& constrFields) $ toConstr rec of
(AlgConstr _, fs) | not $ null fs -> fs
otherwise -> error "Not a record type"

fieldIndex :: (Data r) => String -> r -> Int
fieldIndex fieldName rec =
case fieldName `elemIndex` fieldNames rec of
Just i -> i
Nothing -> error $ "No such field: " ++ fieldName

modifyField :: (Data r, Typeable v) => String -> (v -> v) -> r -> r
modifyField fieldName m rec = gmapTi i (e `extT` m) rec
where
i = fieldName `fieldIndex` rec
e x = error $ "Type mismatch: " ++ fieldName ++
" :: " ++ (show . typeOf $ x) ++
", not " ++ (show . typeOf $ m undefined)

setField :: (Data r, Typeable v) => String -> v -> r -> r
setField fieldName value = modifyField fieldName (const value)

getField :: (Data r, Typeable v) => String -> r -> v
getField fieldName rec = gmapQi i (e `extQ` id) rec
where
i = fieldName `fieldIndex` rec
e x = error $ "Type mismatch: " ++ fieldName ++
" :: " ++ (show . typeOf $ x) ++
", not " ++ (show . typeOf $ e undefined)

关于Haskell根据字段名称字符串动态设置记录字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8659939/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com