5

尽管我用 python 编写,但我认为抽象概念对我和其他人来说更有趣。如果您愿意,请使用伪代码:)

我有一个列表,其中包含我的一个课程中的项目。让我们在这里用字符串和数字来做,这真的没关系。它嵌套到任何深度。(它实际上不是一个列表,而是一个基于列表的容器类。)

示例[1, 2, 3, ['a', 'b', 'c'] 4 ['d', 'e', [100, 200, 300]] 5, ['a', 'b' , 'c'], 6]

请注意,两个 ['a', 'b', 'c'] 实际上是同一个容器。如果你改变一个,你就会改变另一个。可以编辑容器和项目,插入项目,最重要的容器可以多次使用。为了避免冗余,不可能展平列表(我认为!),因为您失去了在一个容器中插入项目的能力,它会自动出现在所有其他容器中。

问题:对于前端(只是带有python“cmd”模块的命令行),我想用一个始终指向当前项目的光标导航这个结构,以便可以读取或编辑它。光标可以左右移动(用户的观点),并且应该表现得像列表不是嵌套列表而是平面列表。

对于人类来说,这非常容易做到。您只是假装在上面的这个列表中子列表不存在,然后简单地从左到右再返回。

例如,如果您在上面列表中的“3”位置并向右走,您会得到“a”作为下一个项目,然后是“b”、“c”,然后是“4”等。或者如果你从“300”接下来是“5”。

向后:如果你从“6”向左走,下一个是“c”。如果你从“5”向左走,它的“300”。

那么原则上我该怎么做呢?我在这里有一种方法,但它是错误的,而且问题已经很长了,我担心大多数人不会阅读它:(。我可以稍后发布。

PS即使很难抗拒:这个问题的答案不是“为什么要这样做,为什么要这样组织数据,为什么不先[扁平化列表|超出我的想象] ? 问题正是我在这里所描述的,仅此而已。数据是按照问题的性质这样构造的。

4

4 回答 4

3

One solution would be to store current index and/or depth information and use it to traverse the nested list. But that seems like a solution that would do a lot of complicated forking -- testing for ends of lists, and so on. Instead, I came up with a compromise. Instead of flattening the list of lists, I created a generator that creates a flat list of indices into the list of lists:

def enumerate_nested(nested, indices):
    for i, item in enumerate(nested):
        if isinstance(item, collections.Iterable) and not isinstance(item, basestring):
            for new_indices in enumerate_nested(item, indices + (i,)):
                yield new_indices
        else:
            yield indices + (i,)

Then a simple function that extracts an innermost item from the list of lists based on an index tuple:

def tuple_index(nested_list, index_tuple):
    for i in index_tuple:
        nested_list = nested_list[i]
    return nested_list

Now all you have to do is traverse the flat index list, in whatever way you like.

>>> indices = list(enumerate_nested(l, tuple()))
>>> print l
[1, 2, 3, ['a', 'b', 'c'], 4, ['d', 'e', [100, 200, 300]], 5, ['a', 'b', 'c'], 6]
>>> for i in indices:
...     print tuple_index(l, i),
... 
1 2 3 a b c 4 d e 100 200 300 5 a b c 6

Since this answer was accepted for the stack-based solution that I posted on ideone in the comments, and since it's preferable not to use external pastebins for answer code, please note that this answer also contains my stack-based solution.

于 2011-07-01T14:41:25.440 回答
3

我会让光标有一堆数组的索引。

例子:

[1, 2, 3, ['a', 'b', 'c'], 4 ]

如果光标位于 1(索引 0 处),则光标的位置为 [0]。

如果光标位于 2(索引 1 处),则光标的位置为 [1]。

如果光标位于“a”处(最外层的索引 3 和第二层的索引 0),则光标的位置将是 [3, 0]。

如果光标位于“b”(最外层的索引 3 和第二层的索引 1),则光标的位置将是 [3, 1]。

等等

要向右移动光标,只需增加该位置最右边的索引。所以当你从 'b' 到 'c' 时,它会从 [3, 1] 增加到 [3, 2]。如果索引超出范围,则从索引堆栈中弹出最右边的值并增加最右边的值。所以如果你从 'c' 到 4,你从 [3, 2] 到 [4]。

移动时,如果遇到有嵌套数组的位置,就输入它。因此,如果您从 3 开始(在索引 [2] 处),则输入数组 ['a','b','c'] 中的第一个元素,该元素位于索引 [3, 0] 处。

向左移动只会与向右移动相反。

于 2011-07-01T14:57:10.103 回答
1

本质上,我会将自己的解决方案基于递归。我将使用以下内容扩展容器类:

  1. cursor_position- 存储突出显示元素(或包含包含突出显示元素的元素的元素,或除此之外的任何级别的递归)的索引的属性。
  2. repr_with_cursor- 此方法应返回容器内容的可打印版本,已突出显示当前选定的项目。
  3. mov_right- 当光标向右移动时应该调用此方法。返回元素内光标的新索引,或者None如果光标落在当前容器“外部”(如果您移过容器中的最后一个元素。
  4. mov_left- 同上,但向左。

递归应该工作的方式是,对于每种方法,根据突出显示的方法的类型,您应该有两种不同的行为:

  • 如果光标在容器上,它应该调用“指向”容器的方法。
  • 如果光标在非容器上,它应该执行“真实的事情”。

编辑 我有半个小时的空闲时间,所以我拼凑了一个示例类来实现我的想法。它的功能不完整(例如,当它到达最大容器的任一端时,它不能很好地处理,并且要求类的每个实例在最大序列中只使用一次),但它足以证明这个概念. 在人们对此发表评论之前,我将重复一遍:这是概念验证代码,它还没有准备好使用!

#!/usr/bin/env python
# -*- coding: utf-8  -*-

class C(list):

    def __init__(self, *args):
        self.cursor_position = None
        super(C, self).__init__(*args)

    def _pointed(self):
        '''Return currently pointed item'''
        if self.cursor_position == None:
            return None
        return self[self.cursor_position]

    def _recursable(self):
        '''Return True if pointed item is a container [C class]'''
        return (type(self._pointed()) == C)

    def init_pointer(self, end):
        '''
        Recursively set the pointers of containers in a way to point to the
        first non-container item of the nested hierarchy.
        '''
        assert end in ('left', 'right')
        val = 0 if end == 'left' else len(self)-1
        self.cursor_position = val
        if self._recursable():
            self.pointed._init_pointer(end)

    def repr_with_cursor(self):
        '''
        Return a representation of the container with highlighted item.
        '''
        composite = '['
        for i, elem in enumerate(self):
            if type(elem) == C:
                composite += elem.repr_with_cursor()
            else:
                if i != self.cursor_position:
                    composite += str(elem)
                else:
                    composite += '**' + str(elem) + '**'
            if i != len(self)-1:
                composite += ', '
        composite += ']'
        return composite

    def mov_right(self):
        '''
        Move pointer to the right.
        '''
        if self._recursable():
            if self._pointed().mov_right() == -1:
                if self.cursor_position != len(self)-1:
                    self.cursor_position += 1
        else:
            if self.cursor_position != len(self)-1:
                self.cursor_position += 1
                if self._recursable():
                    self._pointed().init_pointer('left')
            else:
                self.cursor_position = None
                return -1

    def mov_left(self):
        '''
        Move pointer to the left.
        '''
        if self._recursable():
            if self._pointed().mov_left() == -1:
                if self.cursor_position != 0:
                    self.cursor_position -= 1
        else:
            if self.cursor_position != 0:
                self.cursor_position -= 1
                if self._recursable():
                    self._pointed().init_pointer('right')
            else:
                self.cursor_position = None
                return -1

一个简单的测试脚本:

# Create the nested structure
LevelOne = C(('I say',))
LevelTwo = C(('Hello', 'Bye', 'Ciao'))
LevelOne.append(LevelTwo)
LevelOne.append('!')
LevelOne.init_pointer('left')
# The container's content can be seen as both a regualar list or a
# special container.
print(LevelOne)
print(LevelOne.repr_with_cursor())
print('---')
# Showcase the effect of moving the cursor to right
for i in range(5):
    print(LevelOne.repr_with_cursor())
    LevelOne.mov_right()
print('---')
# Showcase the effect of moving the cursor to left
LevelOne.init_pointer('right')
for i in range(5):
    print(LevelOne.repr_with_cursor())
    LevelOne.mov_left()

它输出:

['I say', ['Hello', 'Bye', 'Ciao'], '!']
[**I say**, [Hello, Bye, Ciao], !]
---
[**I say**, [Hello, Bye, Ciao], !]
[I say, [**Hello**, Bye, Ciao], !]
[I say, [Hello, **Bye**, Ciao], !]
[I say, [Hello, Bye, **Ciao**], !]
[I say, [Hello, Bye, Ciao], **!**]
---
[I say, [Hello, Bye, Ciao], **!**]
[I say, [Hello, Bye, **Ciao**], !]
[I say, [Hello, **Bye**, Ciao], !]
[I say, [**Hello**, Bye, Ciao], !]
[**I say**, [Hello, Bye, Ciao], !]

有趣的问题!我今天最喜欢的操作系统问题!:)

于 2011-07-01T14:37:01.470 回答
1

虽然我喜欢展平索引列表的想法,但这使得在遍历嵌套列表时无法修改任何子列表的长度。如果这不是您需要的功能,我会同意的。

否则,我也会将指向列表的指针作为索引元组实现,并依赖递归。为了让你开始,这里有一个类,它通过 实现right()和读取指针的值deref()。(我None用来表示超出列表末尾的指针。)我将把它作为练习如何实现left()和分配给元素。如果你用另一个列表替换你当前指向的元素,你必须决定你想要什么行为。祝你好运!

def islist(seq):
    return isinstance(seq, (list, tuple))

class nav:
    def __init__(self, seq):
        self.seq = seq
        self.ptr = self.first()
    def __nonzero__(self):
        return bool(self.ptr)
    def right(self):
        """Advance the nav to the next position"""
        self.ptr = self.next()
    def first(self, seq=None):
        """pointer to the first element of a (possibly empty) sequence"""
        if seq is None: seq = self.seq
        if not islist(seq): return ()
        return (0,) + self.first(seq[0]) if seq else None
    def next(self, ptr=None, seq=None):
        """Return the next pointer"""
        if ptr is None: ptr = self.ptr
        if seq is None: seq = self.seq
        subnext = None if len(ptr) == 1 else self.next(ptr[1:], seq[ptr[0]])
        if subnext is not None: return (ptr[0],) + subnext
        ind = ptr[0]+1
        return None if ind >= len(seq) else (ind,) + self.first(seq[ind])
    def deref(self, ptr=None, seq=None):
        """Dereference given pointer"""
        if ptr is None: ptr = self.ptr
        if seq is None: seq = self.seq
        if not ptr: return None
        subseq = seq[ptr[0]]
        return subseq if len(ptr) == 1 else self.deref(ptr[1:], subseq)

abc = ['a', 'b', 'c']
a = [1, 2, 3, abc, 4, ['d', 'e', [100, 200, 300]], 5, abc, 6]
n = nav(a)

while n:
    print n.ptr, n.deref()
    n.right()
于 2011-07-01T14:58:21.243 回答