系统地将函数应用于haskell记录的所有字段 [英] Systematically applying a function to all fields of a haskell record

查看:54
本文介绍了系统地将函数应用于haskell记录的所有字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一条记录,其中包含不同类型的字段,并且有一个适用于所有这些类型的函数。作为一个小(愚蠢的)示例:

  data Rec = Rec {flnum :: Float,intnum :: Int}派生(显示)

我想定义一个在每个字段中添加两个记录的函数:

  addR :: Rec-> Rec-> Rec 
addR ab = Rec {flnum =(flnum a)+(flnum b),intnum =(intnum a)+(intnum b)}

有没有一种方法可以在不重复每个字段的操作的情况下表达这一点(记录中可能有很多字段)?



实际上,我有一个仅由也许字段组成的记录,并且我想将实际数据与包含某些字段默认值的记录结合起来,以当实际数据为 Nothing 时使用。



(我想模板haskell应该可以,但是我对便携式实现更感兴趣。)

解决方案

您可以使用 gzipWithT



我不是专家,所以我的版本有点傻。应该只能调用 gzipWithT 一次,例如使用 extQ extT ,但是我没有找到做到这一点的方法。无论如何,这是我的版本:

  {-#语言DeriveDataTypeable#-} 

导入数据。泛型

数据Test = Test {
test1 :: Int,
test2 :: Float,
test3 :: Int,
test4 :: String,
test5 ::字符串
}
派生(可键入,数据,等式,显示)

t1 ::测试
t1 =测试1 1.1 2 t1 t11

t2 ::测试
t2 =测试3 2.2 4 t2 t22

merge ::测试->测试->测试
merge ab =让b'= gzipWithT mergeFloat ab
b''= gzipWithT mergeInt a b'
在gzipWithT mergeString a b''

mergeInt :: (数据a,数据b)=> -> b-> b
mergeInt = mkQ(mkT(id :: Int-> Int))(\a-> mkT(\b-> a + b :: Int))

mergeFloat ::(数据a,数据b)=> -> b-> b
mergeFloat = mkQ(mkT(id :: Float-> Float))(\a-> mkT(\b-> a + b :: Float))

mergeString ::(数据a,数据b)=> -> b-> b
mergeString = mkQ(mkT(id ::字符串->字符串))(\a-> mkT(\b-> a ++ b ::字符串))

main :: IO()
main = print $ merge t1 t2

输出:

  Test {test1 = 4,test2 = 3.3000002,test3 = 6,test4 = t1t2,test5 = t11t22 } 

代码晦涩难懂,但思路很简单, gzipWithT 将指定的通用函数( mergeInt mergeString 等)应用于一对对应的字段。 / p>

I have a record with fields of different types, and a function that is applicable to all of those types. As a small (silly) example:

data Rec = Rec  { flnum :: Float, intnum :: Int } deriving (Show)

Say, I want to define a function that adds two records per-field:

addR :: Rec -> Rec -> Rec
addR a b = Rec { flnum = (flnum a) + (flnum b), intnum = (intnum a) + (intnum b) }

Is there a way to express this without repeating the operation for every field (there may be many fields in the record)?

In reality, I have a record comprised exclusively of Maybe fields, and I want to combine the actual data with a record containing default values for some of the fields, to be used when the actual data was Nothing.

(I guess it should be possible with template haskell, but I am more interested in a "portable" implementation.)

解决方案

You can use gzipWithT for that.

I'm not an expert, so my version it a bit silly. It should be possible to call gzipWithT only once, e.g. using extQ and extT, but I failed to find the way to do that. Anyway, here is my version:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Generics

data Test = Test {
  test1 :: Int,
  test2 :: Float,
  test3 :: Int,
  test4 :: String,
  test5 :: String
  }
  deriving (Typeable, Data, Eq, Show)

t1 :: Test
t1 = Test 1 1.1 2 "t1" "t11"

t2 :: Test
t2 = Test 3 2.2 4 "t2" "t22"

merge :: Test -> Test -> Test
merge a b = let b' = gzipWithT mergeFloat a b
                b'' = gzipWithT mergeInt a b'
            in gzipWithT mergeString a b''

mergeInt :: (Data a, Data b) => a -> b -> b
mergeInt = mkQ (mkT (id :: Int -> Int)) (\a -> mkT (\b -> a + b :: Int))

mergeFloat :: (Data a, Data b) => a -> b -> b
mergeFloat = mkQ (mkT (id :: Float -> Float)) (\a -> mkT (\b -> a + b :: Float))

mergeString :: (Data a, Data b) => a -> b -> b
mergeString = mkQ (mkT (id :: String -> String)) (\a -> mkT (\b -> a ++ b :: String))

main :: IO ()
main = print $ merge t1 t2

Output:

Test {test1 = 4, test2 = 3.3000002, test3 = 6, test4 = "t1t2", test5 = "t11t22"}

The code is obscure, but the idea is simple, gzipWithT applies the specified generic function (mergeInt, mergeString, etc) to pair of corresponding fields.

这篇关于系统地将函数应用于haskell记录的所有字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆