Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 确保数据的正确性_Haskell_Graph_Types_Gadt - Fatal编程技术网

Haskell 确保数据的正确性

Haskell 确保数据的正确性,haskell,graph,types,gadt,Haskell,Graph,Types,Gadt,我已经在Haskell编程几个月了,我真的很喜欢它。我觉得我对单子、函子、纯度等都有很好的理解。现在我一直在使用这个很好的类型系统,以至于有可能表达不正确的东西的想法听起来很可怕。Haskell允许您有时通过将数据的属性移动到类型系统中来解决这个问题。例如,使用GADT,您可以定义不能以不平衡方式构建的平衡树: 因此,您可以保证在树上实现的任何函数都将生成正确的树。但在其他情况下,我看不到如何在类型级别约束数据 这是我具体考虑的情况。我想表示一个图,其中每个边都指向一个存在的节点。因此,例如,

我已经在Haskell编程几个月了,我真的很喜欢它。我觉得我对单子、函子、纯度等都有很好的理解。现在我一直在使用这个很好的类型系统,以至于有可能表达不正确的东西的想法听起来很可怕。Haskell允许您有时通过将数据的属性移动到类型系统中来解决这个问题。例如,使用GADT,您可以定义不能以不平衡方式构建的平衡树:

因此,您可以保证在树上实现的任何函数都将生成正确的树。但在其他情况下,我看不到如何在类型级别约束数据

这是我具体考虑的情况。我想表示一个图,其中每个边都指向一个存在的节点。因此,例如,如果仅存在节点1-4,则无法定义指向节点5的边。对于DAG样式的图,我知道类似的内容,但对于具有循环的图,我还没有见过类似的内容。我该如何表达这样的东西呢?

通常将(小的)有向图表示为邻接矩阵。如果创建一个n*n矩阵,其中包含
Bool
s,则可以精确地表示具有n个节点的所有图形。当然,您需要一个好的矩阵库来表示它(而不是
[[Bool]]]
,它可能有无效的值),最好是使用类型级别的数字来编码大小,因此您可以要求它们是正方形

一个小例子来说明这一点:

Matrix.hs:

{-#语言签名}
{-#语言数据类型}
{-#语言多样性}
{-#语言范围类型变量#-}
模块矩阵(矩阵、FromList、ToList、代理(..),其中
导入GHC.TypeLits
--是的,我只是说“不要用[[Bool]]”
--但是矩阵构造函数不是导出的,这是唯一的方法
--此API的用户可以使用
--fromLists函数,仅当大小
--匹配预期的矩阵。
--这是一个不好的内存消耗选择,但保持
--例子很简单。
数据矩阵(n::Nat)(m::Nat)a=矩阵[[a]]
派生(显示)
--我们使用这些代理,以便传递类型级别的编号
--作为论据。
数据代理(a::k)=代理
fromLists::(KnownNat n,KnownNat m)
=>代理服务器n
->代理m
->[[a]]
->可能(矩阵n m a)
fromLists proxyN proxyM列表
--将类型级别的数字“降级”为值级别的整数
--并验证输入是否正确。
|fromIntegral(长度列表)=natVal proxyN
,所有(\list->frompintegral(length list)==natVal proxyM)列表
=仅(矩阵列表)
从列表_
没有
toLists::矩阵n m a->[[a]]
ToList(矩阵列表)=列表
图1.hs:

{-#语言数据类型}
导入矩阵
导入GHC.TypeLits
--表示具有n个顶点的图。
类型图n=矩阵n布尔
--将图形转换回邻接列表。
toEdgeList::(KnownNat n)=>图n->[(整数,[Integer])]
趾缘图
=让
邻接=托利斯图
在zipWith(\i行->(i,映射fst$filter snd$zip[0..]行))[0..]邻接中
main=do
案例列表(Proxy::Proxy 4)(Proxy::Proxy 4)
[True,False,False,True]
,[假,真,假,假]
,[假,假,假,假]
,[真的,真的,真的,真的]
]的
(仅图形)->打印(toEdgeList图形)
结果:

[(0,[0,3]),(1,[1]),(2,[]),(3,[0,1,2,3])]
这种方法并没有使无效的图不可表示,它只是通过隐藏
矩阵
构造函数和仅将
fromLists
作为“智能构造函数”公开,使它们不可构造。只要从Matrix.hs导出的所有函数都保持不变量,这是安全的


当图形很大但很稀疏,并且无法在内存中构建整个邻接矩阵时,可以返回邻接列表。我们可以使用相同的类型级别技巧来创建可在此处使用的有界自然数:

{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
import GHC.TypeLits

data BoundedInteger (n :: Nat) = BoundedInteger Integer

instance Show (BoundedInteger n) where
    show (BoundedInteger i) = show i

data Proxy (n :: k) = Proxy

boundedFromInteger :: (KnownNat n) => Proxy n -> Integer -> Maybe (BoundedInteger n)
boundedFromInteger proxy n
    | 0 <= n
    , n <= natVal proxy
    = Just (BoundedInteger n)
boundedFromInteger _ _ = Nothing

如果您不给节点编号,而是通过某种合适的有限(或可能无限!)类型对它们进行完全索引,那么您可以很好地完成这项工作。要在这样一个图中存储每个节点的数据,可以使用以下方法

然后,有向边的类型只是一个元组
(i,i)
。因为节点集恰好是
i
中所有可能值的集合,所以这保证在图中

这种类型的完整图形的有效存储方式就是从节点到节点的
映射

newtype GraphEdges i = GraphEdges { getGraphEdges :: Map.Map i i }

这是一个好主意,如果你展示一些代码,我想这将是一个很好的答案@BenjaminHodgson我对Haskell中的矩阵没有任何直接的经验,但我会尝试一下。
newtype GraphNodes i d = GraphNodes { getGraphNodes :: i :->: d }
newtype GraphEdges i = GraphEdges { getGraphEdges :: Map.Map i i }