我想编写一些适用于Haskell中所有数据类型的函数(至少来自Data.Data的所有数据实例)。我遇到的一个普遍问题是根据构造函数是否递归来选择做什么 - 通过递归构造函数我的意思是数据类型是D并且有一个构造函数C,其参数是D。
我可以通过解决一个更简单的问题来解决上述问题:给定一个数据,确定最外面的构造函数是否是递归的。
以下是尝试编号1:
data B a = T | F
gIsLeafCons :: (Data a) => a -> B a
gIsLeafCons m = gfoldl (\x y -> F) (\x -> T) m
这个想法是,如果构造函数是递归的,那么我们返回F,否则我们返回T.这看起来很有用:
gIsLeafCons 1 ==> T
gIsLeafCons ([] :: [Int]) ==> T
但是,gfoldl是多态的,所以给定一个树数据类型,如
data Tree a = Leaf a | Node (Tree a) (Tree a)
我们失败了。
gIsLeafCons (Leaf 1) ==> F
原因是(Leaf 1)不是一个真正的空构造函数:它有参数1,因此应用了构造函数(\ x y - > F)。
尝试2号:
我们可以选择"叶子构造函数"是所有孩子评价为F的一个。这是一个小小的改进:
gIsLeafByChild (Leaf 1) ==> T
但是,如果叶子具有不同的递归结构;这将再次失败。
gIsLeafByChild (Leaf (Leaf 1)) ==> F
我真的想停在第一个"叶子构造函数"但是gfoldl的多态性使得它看起来很难,如上所示。
最后,有没有办法可以说明构造函数是否是一个"叶子构造函数"或不。我目前的解决方案是让用户传递叶子构造函数列表([Constr]),我只是检查构造函数是否是其中之一。但是,自动推断某些内容是否在此列表中会很好。
答案 0 :(得分:1)
您使用gfoldl
已经有了一个良好的开端。我们需要进行两项更改。首先,当我们遍历结构时,我们需要跟踪每个遇到的类型以查看它们是否以递归方式出现。其次,我们需要递归地使用gfoldl
来查看我们在构造函数中发现的类型。
让我们获得一些实用工具。我们完成后会bool
获得Bool
结果,bId
会将B
从一种类型转换为另一种类型。
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}
import Data.Data
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving (Data, Typeable)
data B a = T | F
bool :: B a -> Bool
bool T = True
bool F = False
bId :: B a -> B b
bId T = T
bId F = F
我们可以从TypeRep
类中获取类型的表示形式或Typeable
。 Typeable
是Data
的超类,因此每当我们为某些Data a
设置a
个实例时,我们也会有Typeable a
个实例。我们通过携带包含类型的列表来跟踪这些内容。 [TypeRep]
中的表示。
recursivelyConstructed :: Data a => a -> Bool
recursivelyConstructed = bool . rc []
where
cf :: forall d. d -> B d
cf = const F
rc :: forall d. Data d => [TypeRep] -> d -> B d
rc ts d = gfoldl (rc' (typeOf d:ts)) cf d
rc' :: forall d b. Data d => [TypeRep] -> B (d -> b) -> d -> B b
rc' _ T _ = T
rc' ts F d =
if elem (typeOf d) ts
then T
else bId . rc ts $ d
让我们尝试一些例子
infiniteTree = Node infiniteTree infiniteTree
recursivelyConstructed (Leaf 1 :: Tree Int) = False
recursivelyConstructed (Node (Leaf 1) (Leaf 2) :: Tree Int) = True
recursivelyConstructed (infiniteTree :: Tree Int) = True
recursivelyConstructed (Leaf (Leaf 1) :: Tree (Tree (Int))) = False
recursivelyConstructed (Just (Leaf 1) :: Maybe (Tree Int)) = False
recursivelyConstructed (Just (Node (Leaf 1) (Leaf 2)) :: Maybe (Tree Int)) = True
recursivelyConstructed (Just infiniteTree :: Maybe (Tree Int)) = True
recursivelyConstructed (Just (Leaf (Leaf 1)) :: Maybe (Tree (Tree Int))) = False