如何在Haskell中创建点阵型数据结构?
How to create a lattice-type data structure in Haskell?
我正在尝试在 Haskell 中构建 FCA 类型的格型数据结构,我可以在其中检查两个实体是否有连接。实际上,我什至不确定格子是否是正确的结构,因为它可能有点“太多”了。
这是上下文。在 COBOL 程序分析器中,我有一个 ontology 的数据集名称、文件、记录和字段。根据程序的不同,一个数据集名称可以有多个文件名,一个文件可以有多个记录,一个记录可以有多个字段。我希望此层次结构反映在 Haskell 数据结构中。但我也希望能够为 file1 和 file2 继承关系,以便我可以检查 file1 和 file2 是否属于同一数据集名称。实际上,这种关系几乎可以是“==”的关系。但这可能仅仅是因为他们在 dsn0 中确实有一个连接,例如。
在那种情况下,我还有其他本体可以从格或 FCA 数据结构中受益。例如,我有属于作业步骤的程序和属于作业的作业步骤。如果我能很容易地弄清楚两个程序是否属于同一个工作,那就太好了。在这里,它似乎也是一个“连接”运算符。获取某个实体的扩展名(代码)也很有用。
我对 Haskell 还是有点陌生。我试着查看 the Lattice library 但我不确定从那里具体去哪里。知道如何开始吗? Haskell 中的格子的一个小例子会很有帮助。非常感谢您的帮助(和耐心)。
更新:
如评论中所述,格子可能不是最好的形式主义。我意识到我可能只需要使用常规的 class 类型的数据结构:
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
}
我想我使用 tree/lattice/FCA 类型结构的最初意图是充分利用 Haskell 中的函子潜力,这应该会导致有趣的格运算,包括获得 a 的所有扩展概念,检查两个概念是否属于同一个更高级别的概念,通过它们的 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。但这是最小的 construct/consume 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 :: b
和 x' :: a <-> y' :: b
,以及 f b = f b'
,那么应该 fmap f
将 f b
与 a
或 a'
?如果它们确实具有合理的解释并且有用,您可以采用受约束的 monads 方法,或者简单地从具有适当类型的 OneToMany
模块中提供一个名为 bimap
的函数,但不要使它是一个实例方法。
我正在尝试在 Haskell 中构建 FCA 类型的格型数据结构,我可以在其中检查两个实体是否有连接。实际上,我什至不确定格子是否是正确的结构,因为它可能有点“太多”了。
这是上下文。在 COBOL 程序分析器中,我有一个 ontology 的数据集名称、文件、记录和字段。根据程序的不同,一个数据集名称可以有多个文件名,一个文件可以有多个记录,一个记录可以有多个字段。我希望此层次结构反映在 Haskell 数据结构中。但我也希望能够为 file1 和 file2 继承关系,以便我可以检查 file1 和 file2 是否属于同一数据集名称。实际上,这种关系几乎可以是“==”的关系。但这可能仅仅是因为他们在 dsn0 中确实有一个连接,例如。
在那种情况下,我还有其他本体可以从格或 FCA 数据结构中受益。例如,我有属于作业步骤的程序和属于作业的作业步骤。如果我能很容易地弄清楚两个程序是否属于同一个工作,那就太好了。在这里,它似乎也是一个“连接”运算符。获取某个实体的扩展名(代码)也很有用。
我对 Haskell 还是有点陌生。我试着查看 the Lattice library 但我不确定从那里具体去哪里。知道如何开始吗? Haskell 中的格子的一个小例子会很有帮助。非常感谢您的帮助(和耐心)。
更新: 如评论中所述,格子可能不是最好的形式主义。我意识到我可能只需要使用常规的 class 类型的数据结构:
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
}
我想我使用 tree/lattice/FCA 类型结构的最初意图是充分利用 Haskell 中的函子潜力,这应该会导致有趣的格运算,包括获得 a 的所有扩展概念,检查两个概念是否属于同一个更高级别的概念,通过它们的 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。但这是最小的 construct/consume 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 :: b
和 x' :: a <-> y' :: b
,以及 f b = f b'
,那么应该 fmap f
将 f b
与 a
或 a'
?如果它们确实具有合理的解释并且有用,您可以采用受约束的 monads 方法,或者简单地从具有适当类型的 OneToMany
模块中提供一个名为 bimap
的函数,但不要使它是一个实例方法。