在Python中查找列表边界

2 投票
2 回答
597 浏览
提问于 2025-04-18 10:22

给定一系列点(通过路径算法找到的),我想知道进出某个区域的点。区域外的点用 None 表示。比如,一个路径可以是 [None, 1, 2, None, 3, 4, 5, None],这个路径的结果应该是 [(1,2), (3,5)]。对于一个特殊情况 [1, 2, None],结果应该是 [(None, 2)];而对于镜像情况 [None, 1, 2],结果应该是 [(1, None)]

如果不考虑最后两个特殊情况,我得到了:

[(systems[0], systems[-1]) if not outside else None
 for outside, systems in [(o, list(sys))
 for o, sys in groupby(path, lambda x: x is None)]]

或者通过 IRC

def find_borders(L):
    it = iter(L)
    for item in it:
        if item is None:
            continue
        start = item
        end = item
        for item in it:
            if item is None:
                break
            end = item
        yield (start, end)

通过IRC的解决方案,开头加上 None 很简单,但在结尾加上几乎是不可能的,除非对 iter 进行一次 peek 操作。

我该如何实现第二个特殊情况 [None, 1,2] => [(1, None)]

我创建了一个应该有效的解决方案,但实际上并没有。它给我抛出了一个 IndexError,因为 systems 是一个空列表,这不应该发生,因为 groupby 保证不会分组成一个空列表。

def find_borders(path):
    grouped = list(groupby(path, lambda x: x is None))
    for index, (o, sys) in enumerate(grouped):
        systems = list(sys)
        if index == 0:
            yield (None, systems[-1])
        elif index == len(grouped)-1:
            yield (systems[0], None)
        else:
            yield (systems[0], systems[-1])

2 个回答

0

更优雅的写法:

def find_borders(L):
    entries = []
    exits = []
    if L[0] is not None:
        entries.append(None)

    for i in range(len(L[:-2])):
        if L[i] is None:
            entries.append(L[i+1])
        if L[i+2] is None:
            exits.append(L[i+1])

    if L[-1] is not None:
        exits.append(None)
    return zip(entries, exits)
2
tests = [
    [None, 1, 2, None, 3, 4, 5, None],
    [1, 2, None, 3, 4, 5, None],
    [None,1, 2, None, 3, 4, 5],
    [1, 2, None, 3, 4, 5],
    [1, 2, None],
    [None, 1, 2],
]

def find_borders(L):
    it = iter(L)

    start_none = False

    for item in it:
        if item is None:
            start_none = True
            continue
        if start_none:
            start = item
        else:
            start = None

        end = item

        end_none = False

        for item in it:
            if item is None:
                end_none = True
                break
            end = item
        if not end_none:
            end = None
        yield (start, end)

        start_none = True

for t in tests:
    print
    print t
    for x in find_borders(t):
        print x
[None, 1, 2, None, 3, 4, 5, None]
(1, 2)
(3, 5)

[1, 2, None, 3, 4, 5, None]
(None, 2)
(3, 5)

[None, 1, 2, None, 3, 4, 5]
(1, 2)
(3, None)

[1, 2, None, 3, 4, 5]
(None, 2)
(3, None)

[1, 2, None]
(None, 2)

[None, 1, 2]
(1, None)

结果

撰写回答