【问题标题】:How to create a lattice-type data structure in Haskell?如何在 Haskell 中创建格型数据结构?
【发布时间】:2021-04-18 22:53:26
【问题描述】:

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

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

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

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

更新: 正如 cmets 中提到的,Lattice 可能不是最好的形式。我意识到我可能只需要按照这些思路使用常规类类型的数据结构:

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 中很容易做到这一点吗?

【问题讨论】:

  • 就我理解你想要做什么的程度而言......听起来“属于”的感觉有所不同;并且一个实体可能以不同的方式属于其他实体(或者可能以多种方式属于同一个实体?)然后我不确定格子是正确的结构——所有的“属于”都是同一个意义上的。我会将所有链接展平到关系数据库中。编程语言内的数据结构似乎不是适合这项工作的工具。
  • 一个数据集可以有多个文件。一个文件可以属于多个数据集吗?如果不是,您是否希望将该约束融入到类型中,或者您是否愿意自己维护一个未经编译器检查的不变量?
  • 感谢@DanielWagner 提出这些非常好的澄清问题。事实上,格子可能不是最好的形式。我意识到我可能只需要使用常规类类型的数据结构:DSN = DSN { programFiles :: [ProgramFile] name :: String ddn :: DDN } data ProgramFile = ProgramFile { records :: [Record] name :: String } ... 我想我使用树/格/FCA 类型结构的最初意图是充分利用 Haskell 中的函子潜力,这应该会导致有趣的格操作。
  • 感谢@AntC 的评论。我在上面回答,因为两个 cmets 都朝着相同的方向前进。
  • @HugolinBergier 我不认为您的编辑或评论实际上回答了我的澄清问题。你能再刺他们一次吗?我有几个具体的解决方案,但哪一个最合适取决于这些问题的答案。

标签: haskell ontology lattice formal-semantics


【解决方案1】:

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

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

和朋友。

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

【讨论】:

  • 谢谢@DanielWagner,这太完美了!我认为这正是我需要开始的。对于这些类型的关系没有标准库或内置函数,我感到有点惊讶。但是你提供的更好,因为我可以根据自己的需要定制它并完全理解它。
  • 我现在可以看到仿函数对这种数据结构没有多大意义。也许这不是我需要的。我想到的一个例子是,如果我们一方面有程序本体的层次结构(使用 OneToMany),另一方面有源代码引用的层次结构(偏序),并且我们想在两者之间建立函子。但我需要先在这方面继续前进,以便更多地思考和澄清事情。再次,非常感谢!
  • @HugolinBergier Hackage 有一个many-to-many relations 的数据结构和一个one-to-one relations 的数据结构,但是对于多对一来说似乎确实存在漏洞。多对多结构可以用作OMRel 的替代实现技术;也许它会涉及更少的样板,尽管它的效率也可能会略低一些。也许你会是上传一个填补这个漏洞的包的人!
  • 哈!一旦我像你一样精通 Haskell,也许是的......小问题:插入的类型不是缺少参数(或值)吗?也许我们应该在行尾添加'-> OMRel a b'?
  • @HugolinBergier 如果有六个小错误,我不会感到惊讶——我或多或少地输入了整个模块,并没有实际检查编译器是否同意和我一起,更不用说测试它的功能了。如果我知道正确的措辞是什么,这似乎是一个放置关于不适合任何用途或其他法律术语的好地方。真正的目标是将一个想法传达给人类,所以我没有费心去检查我是否已经将这个想法传达给了机器。
猜你喜欢
  • 2021-01-25
  • 2018-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-25
相关资源
最近更新 更多