[英]Logically sorting a list of lists (partially ordered set -> topological sort)
[英]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)
表示b
是a
的祖先目录,例如lt(/root/dir1, /root)
为True
。
使用CollapsibleSubset的想法是以最简洁的方式表示一组路径,即尽可能少的元素。 例如CollapsibleSubset({/root/dir1, /root/dir2})
将代表dir1
, dir2
中的所有文件和目录。
部分有序集合U的CollapibleSubset A是U中满足两个要求的元素集合:
“可折叠性”。
,其中dp是直接前导关系:
换句话说,如果一个元素的所有前辈都属于该子集,那么该元素本身必须属于该子集。
例子。 {root/dir1/file11.txt, /root/dir1/file12.txt}
不是有效的 CollapibleSubset,但{/root/dir1}
是。
没有可比的元素
换句话说,如果一个元素属于子集,那么它的前任都不能。
例子。 {/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'}))
实践中的部分顺序可以由 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.