繁体   English   中英

部分有序集的可折叠子集

[英]Collapsible subset of partially ordered set

问题

我正在寻找部分有序通用集的一种特殊子集的 python 实现。 它很特别,因为这个子集(我称之为CollapsibleSubset )可以包含的通用集中的哪些元素受到限制。 CollapsibleSubset必须满足 1) “可折叠性”和 2)“不存在可比较元素”的要求。 与普通集合相比, CollapsibleSubsets上的操作也有不同的定义。 我将用文件系统路径的例子来说明正式的定义。

示例 - 文件系统路径集

考虑一个文件系统树:

/root/
├── dir1/
│   ├── file11.txt
│   └── file12.txt
├── dir2/
│   ├── file21.txt
│   └── file22.txt
└── file0.txt

在这种情况下,通用集都是有效路径 { /root , /root/dir1 , /root/dir1/file11.txt .. /root/file0.txt }。 偏序lt由目录结构定义,即lt(a,b)表示ba的祖先目录,例如lt(/root/dir1, /root)True

使用CollapsibleSubset的想法是以最简洁的方式表示一组路径,即尽可能少的元素。 例如CollapsibleSubset({/root/dir1, /root/dir2})将代表dir1 , dir2中的所有文件和目录。

定义

部分有序集合UCollapibleSubset AU中满足两个要求的元素集合:

  1. “可折叠性”。

    ,其中dp是直接前导关系:

    换句话说,如果一个元素的所有前辈都属于该子集,那么该元素本身必须属于该子集。

    例子。 {root/dir1/file11.txt, /root/dir1/file12.txt}不是有效的 CollapibleSubset,但{/root/dir1}是。

  2. 没有可比的元素

    换句话说,如果一个元素属于子集,那么它的前任都不能。

    例子。 {/root/dir1, /root/dir1/file12.txt}不是有效的 CollapibleSubset,但{/root/dir1}是。

要求 1 和 2 确保子集包含尽可能少的元素。

运营

联盟

A,B - CollapsibleSubsets, C=Union(A,B) 如果 C 是有效的 CollapsibleSubset 并且:

例子。

CollapsibleSubset({'/root/dir1', '/root/dir2/file21.txt'}).union(
    CollapsibleSubset({'/root/dir2/file22.txt', '/root/file0.txt'}
) = CollapsibleSubset({'/root'})

补充

A - CollapsibleSubset, B = Complement(A) 如果 B 是有效的 CollapsibleSubset 并且:

例子。

CollapsibleSubset({'/root/dir2/file21.txt'}).complement()=
CollapsibleSubset({'/root/dir1', '/root/dir2/file22.txt', '/root/file0.txt'}))

可能的 CollapsibleSubset 创建 API

实践中的部分顺序可以由 supremum 和返回直接前辈的 function 指定。 对于路径集示例:

>>> colsub = CollapsibleSubset(sup='/root', pred_func=lambda p: os.listdir(p), elements={'/root/dir1', '/root/file0'})

另一个用例 - 任意层次子集

文件系统示例并不是我唯一使用CollapsibleSubset class 有用的用例。 另一种是以最快的方式定义任意层次结构的子集。

例子。 具有以下层次结构:

                Commit
             /     |    \
           /      Fix     \
         /       /   \      \
  Feature   CodeFix  DocFix  Refactoring
   /          /        \           \
 ....       ....       ....        ...

定义为字典:

>>> hierarchy = {'Commit': ['Feature', 'Fix', 'Refactoring'], 'Fix': ['CodeFix', 'DocFix'], ...}

我想以最简短的方式定义它的一个子集。 例如,使用CollapsibleSubset定义不同类型的修复或重构的所有类型的提交的方法是:

>>> CollapsibleSubset('Commit', lambda p: hierarchy[p], {'Fix', 'Refatoring'})

问题

这样的CollapsibleSubset抽象是否已经在任何库中实现?

我从来没有听说过这样的抽象,但我们可以自己写。 首先,我们需要一些结构来存储订单和有用信息:

class Node:
    def __init__(self, children=None, name=""):
        self.parent = None  # parent Node
        self.dotted_name = name  # full name with dots separated elements
        self.name = name
        self.children = {c.name: c for c in children or []}
        for c in self.children.values():
            c.parent = self
            c.dotted_name = f"{self.name}.{c.name}" if self.name else c.name
        self.taken = set()  # Set of already taken children for specific node
        self.locked = False  # Indicates that some of parent is already taken
        self.marked = (
            False  # Used in complement to mark all Nodes that have children already in Set
        )

    def add_to_non_locked(self, name):
        if not self.locked:
            self.taken.add(name)

    @property
    def is_complete(self):
        return len(self.taken) == len(self.children)  # We should collapse here

    def change_lock(self, val):
        """ Change locked attribute recursively """
        self.locked = val
        for c in self.children.values():
            c.change_lock(val)

    def clear(self):
        """ Clear Node and children, used in collapse """
        self.taken.clear()
        for c in self.children.values():
            c.clear()

    def mark(self, mark_parent=True, mark_children=True):
        """ Used in complement """
        if self.marked:
            return
        self.marked = True
        if self.parent and mark_parent:
            self.parent.mark(mark_children=False)
        if mark_children:
            for c in self.children.values():
                c.mark(mark_parent=False)

    def get_non_marked(self):
        """ Traverse every Node and take non marked which is what complement should returs """
        if not self.marked:
            res = [self]
        else:
            res = [x for c in self.children.values() for x in c.get_non_marked()]
        self.marked = False
        return res

    def __str__(self):
        ll = [str(c) for c in self.children.values()]
        return f"{self.name}{ll if ll else ''}"


# Creates example structure
f11 = Node(name="f11")
f12 = Node(name="f12")
f21 = Node(name="f21")
f22 = Node(name="f22")
dir1 = Node({f11, f12}, name="dir1")
dir2 = Node({f21, f22}, name="dir2")
file0 = Node(name="file0")
root = Node({dir1, dir2, file0})

现在是正确设置的时候了:

class CollapsibleSubset(set):
    def __init__(self, *args, root: Node, **kwargs):
        super().__init__(*args, **kwargs)
        self.root = root

    def add_el(self, el: list):
        """ Assuming element to add is in form list of elements """
        last = self.get_node(el).parent
        last.add_to_non_locked(el[-1])
        if last.is_complete:
            for t in last.taken:
                self.discard(
                    ".".join([*el[:-1], t])
                )  # Removing all elements from set as we need to collapse
            last.clear()
            last.change_lock(True)
            self.add_el(el[:-1])  # Running recursively for parent
            return
        self.add(".".join(el))

    def union(self, other):
        for el in other:
            self.add_el(el.split("."))

    def complement(self):
        for el in self:
            self.get_node(el).mark()
        x = self.root.get_non_marked()
        return CollapsibleSubset([a.dotted_name for a in x], root=deepcopy(root))

    def get_node(self, el):
        if isinstance(el, str):
            el = el.split(".")
        return reduce(lambda x, y: getattr(x, "children")[y], el, self.root)


s = CollapsibleSubset(root=deepcopy(root))
s2 = CollapsibleSubset(root=deepcopy(root))
s.add_el(["dir1", "f11"])
s2.add_el(["dir1", "f12"])
s2.add_el(["dir2", "f21"])
s.union(s2)
print(s) # CollapsibleSubset({'dir2.f21', 'dir1'})
sc = s.complement()
print(sc) # CollapsibleSubset({'dir2.f22', 'file0'})

如果有什么我应该进一步解释的,请告诉我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM