我正在寻找像(在F#中)的转换:
type Test = TBool of bool | TString of string
type TestList = TLBool of bool list | TLString of string list
let transform : Map<int, Test> list -> Map<int, TestList> = ??
有没有办法对此进行编码,以便我们“知道”当Map包含异构值时,每个位置的值在包含列表的元素中是相同的类型?一旦构造,地图将是静态大小,并且每个列表元素都是相同的,但是事先不知道大小,所以我基本上想要生成未知大小的元组/记录。
修改
我认为我的例子不清楚。我所追求的根源是能够采用两个变量大小的集合,它们在给定位置的值总是相同的类型,但集合本身可以包含多种类型的值,并使用它们将它们“压缩”在一起。知道在给定位置两个值是相同的类型。具体来说,我不想重新检查它们是否相同并传播它们变化的条件(作为某种错误),因为我在最初创建集合时已经完全这样做了。
修改2
从下面发布的评论:我本质上想要异构列表(我使用地图,因为我的索引可以是稀疏的,但我总是可以使用带有索引映射的列表),但是附加约束是异构列表的两个实例可以“压缩在一起”,并且给定索引处的值具有相同的类型。
答案 0 :(得分:1)
编辑 [...]我所追求的根源是能够获取两个可变大小的集合,它们在给定位置的值始终是相同的类型,但是该集合本身可以包含多种类型的值,并使用在给定位置两个值是相同类型的知识将它们“压缩”在一起。
在最广泛的阅读中,这从根本上要求集合的类型编码哪个位置包含哪个元素类型,并且这会侵入依赖类型区域。
如果有一个固定形状的集合可以在编译时确定,那么很容易 - 你只需要写一个类型,其值都具有该形状。
答案 1 :(得分:0)
您可以定义一个具有所需属性的transformMap
函数(即保留键并将字符串转换为字符串列表并将bool转换为bool列表)。该函数仍然可以完全参数化它与值的关系:
let transformMap fstr fbool map =
map |> Map.map (fun k v ->
match v with
| TBool b -> TLBool(fbool k b)
| TString s -> TLString(fstr k s) )
您使用transformMap
在地图上执行的任何转化都具有您想要的属性。如果你想要更严格,你可以在Map<'K, TestList>
上写一个包装器并隐藏内部表示,这将给你一个强有力的保证。在实践中,我认为公开内部结构可能是合理的,但是手动检查您只使用正确的转换函数操作地图。
使用此函数将每个值转换为单个列表的示例如下所示:
Map.empty |> transformMap
(fun k s -> [s])
(fun k b -> [b])
答案 2 :(得分:0)
如果您可以使用Test list
代替TestList
,则转换相当简单:
let transform listOfMaps : Map<int, Test list> =
listOfMaps
|> Seq.collect Map.toSeq
|> Seq.groupBy fst
|> Seq.map (fun (i, s) ->
let xs = s |> Seq.map snd |> Seq.toList
i, xs)
|> Map.ofSeq
至于在类型中捕捉你的假设,我怀疑它不可能或者比它的价值更多的努力。
答案 3 :(得分:0)
包含在某些f
中的元素的异构列表是:
{-# LANGUAGE GADTs, DataKinds, TypeOperators #-}
infixr 5 :::
data HList f as where
HNil :: HList f '[]
(:::) :: f a -> HList f as -> HList f (a ': as)
例如,
xs1 :: HList [] [Bool, Int]
xs1 = [True, False] ::: [1..3] ::: HNil
xs2 :: HList [] [Bool, Int]
xs2 = [True] ::: [1..5] ::: HNil
可以轻松压缩两个HList []
:
hzip :: HList [] as -> HList [] as -> HList [] as
hzip HNil HNil = HNil
hzip (xs ::: xss) (ys ::: yss) = (xs ++ ys) ::: hzip xss yss
或更普遍(并使用Rank2Types
扩展名):
hzipWith :: (forall a. f a -> g a -> h a) -> HList f as -> HList g as -> HList h as
hzipWith f HNil HNil = HNil
hzipWith f (x ::: xs) (y ::: ys) = f x y ::: hzipWith f xs ys
hzip :: HList [] as -> HList [] as -> HList [] as
hzip = hzipWith (++)
然后(需要FlexibleInstances
和FlexibleContexts
)
instance Show (HList f '[]) where
show HNil = "HNil"
instance (Show (f a), Show (HList f as)) => Show (HList f (a ': as)) where
show (x ::: xs) = show x ++ " ::: " ++ show xs
main = print $ hzip xs1 xs2
打印[True,False,True] ::: [1,2,3,1,2,3,4,5] ::: HNil
。