微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 Haskell 中创建格型数据结构?

如何解决如何在 Haskell 中创建格型数据结构?

我正在尝试在 Haskell 中构建 FCA 类型的数据结构的格子类型,我可以在其中检查两个实体是否有连接。实际上,我什至不确定格子的结构是否正确,因为它可能有点“太多”。

这是上下文。在 COBOL 程序分析器中,我有一个数据集名称文件、记录和字段的本体。根据程序的不同,一个数据集名称可以有多个文件名,一个文件可以有多个记录,一个记录可以有多个字段。我希望这个层次结构反映在 Haskell 数据结构中。但我还希望能够为 file1 和 file2 继承关系,以便我可以检查 file1 和 file2 是否属于相同的数据集名称。实际上,这种关系几乎可以是“==”的关系。但也可能只是因为他们确实加入了 dsn0。

在这种情况下,我还有其他本体可以从网格或 FCA 数据结构中受益。例如,我有属于作业步骤的程序和属于作业的作业步骤。如果我能很容易地确定两个程序是否属于同一个工作,那就太好了。在这里,它似乎也是一个“连接”运算符。获取某个实体的扩展(代码)也会很有用。

我对 Haskell 还是有点陌生​​。我试图查看 the Lattice library,但具体而言,我不知道从哪里开始。知道如何开始吗? Haskell 中格子的一个小例子会很有帮助。非常感谢您的帮助(和耐心)。

更新: 正如评论中提到的,格子可能不是最好的形式主义。我意识到我可能只需要沿着这些路线使用常规类类型的数据结构:

data DSN = DSN {
    programFiles :: [ProgramFile]
    name :: String
    ddn :: DDN
}
data ProgramFile = ProgramFile {
    records :: [Record]
    name :: String
}
data Record = Record {
    fields :: [Field]
    name :: String
}
data Field = Field {
    name :: String
    order :: Int
}

我想我使用树/格/FCA类型结构背后的最初意图是充分利用Haskell中的函子势,这应该会导致有趣的格操作,包括获得一个概念的所有扩展,检查两个概念属于同一个更高级别的概念,通过它们的 DSN 检查两个文件相等性 '==',...

也许非二叉树结构会更好?在 Haskell 中这样做容易吗?

解决方法

我建议使用抽象数据类型来表示一对多关系。它可能看起来像这样:

module OneToMany (OMRel,empty,insert,delete,source,targets) where

import Data.Map (Map)
import Data.Set (Set)
import qualified Data.Map.Strict as M
import qualified Data.Set as S

data OMRel a b = OMRel
    { oneToMany :: Map a (Set b),manyToOne :: Map b a
    } deriving (Eq,Ord,Read,Show)

empty :: OMRel a b
empty = OMRel M.empty M.empty

insert :: (Ord a,Ord b) => a -> b -> OMRel a b -> OMRel a b
insert a b (OMRel otm mto) = OMRel
    { oneToMany = M.insertWith S.union a (S.singleton b) $
        case M.lookup b mto of
            Just oldA -> M.adjust (S.delete b) oldA otm
            Nothing -> otm,manyToOne = M.insert b a mto
    }

delete :: (Ord a,Ord b) => a -> b -> OMRel a b -> OMRel a b
delete a b (OMRel otm mto) = OMRel (M.adjust (S.delete b) a otm) (M.delete b mto)

source :: Ord b => b -> OMRel a b -> Maybe a
source b = M.lookup b . manyToOne

targets :: Ord a => a -> OMRel a b -> Set b
targets a = M.findWithDefault S.empty a . oneToMany

(当然,您可以使用更高效的批量操作来充实 API,例如合并、批量插入、顺序组合等。但这是一种最小的构造/使用 API,可以让您到达需要的地方。)

然后您需要几种数据类型来表示您的各种本体条目:

newtype Dataset = Dataset { dataset :: String }
newtype Record = Record { record :: String }
newtype Field = Field { order :: Int }

从那里您可以使用类型为 OMRel Dataset FilePath 的值来表示数据集“包含”文件的事实。要查询包含相等性,您可以通过上面的 OMRel API 一劳永逸地编写此代码:

sameSource :: (Eq a,Ord b) => OMRel a b -> b -> b -> Bool
sameSource rel b b' = source b rel == source b' rel

(如果两个缺失的目标应该被认为是不相等的,你可能需要一个额外的子句。)然后,例如,这可以专门用于

sameSource :: OMRel Dataset FilePath -> FilePath -> FilePath -> Bool

和朋友。

由于 Functor 限制,您将无法为 Bifunctor 创建 OMRel(或 Ord)实例。不过,我不清楚 fmap/bimap 对这个特定的数据结构有多大意义。例如,如果我们有关联 x :: a <-> y :: bx' :: a <-> y' :: b,以及 f b = f b',那么应该 fmap ff ba 或 {{1} }?如果它们确实有合理的解释并且有用,您可以采用受约束的 monads 方法,也可以简单地从具有适当类型的 a' 模块中提供一个名为 bimap 的函数,但无需使它是一个实例方法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。