Event尽管我用python写作,但我认为抽象概念对我和其他人来说更有趣。因此,如果你喜欢,请使用伪代码:)
我有一个清单,里面有我的一个班的项目。让我们在这里用字符串和数字来做,这真的不重要。它嵌套到任何深度。(它实际上不是一个列表,而是一个基于列表的容器类。)
示例:[1,2,3,[‘a’,‘b’,‘c’]4[’d’,‘e’,[1000200300]]5,[’a’,’b’,’c’],6]
请注意,两个[a','b','c']实际上是同一个容器。如果你改变了一个,你就改变了另一个。容器和项目可以编辑,项目可以插入,最重要的容器可以多次使用。为了避免冗余,不可能使列表变平(我认为!),因为您失去了在一个容器中插入项目的能力,而它会自动显示在所有其他容器中。
问题:对于前端(只是带有python"cmd"模块的命令行),我希望使用始终指向当前项的光标浏览此结构,以便可以读取或编辑它。光标可以向左和向右移动(用户的观点),并且应该表现为列表不是嵌套列表而是平面列表。
对于一个人来说,这是非常容易做到的。你只需要假装上面的列表中的子列表不存在,然后从左到右再回来。
例如,如果你在上面的列表中的"3"位置,向右走,你会得到"a"作为下一个项目,然后是"b"、"c",再是"4"等。或者,如果你从"300"向右走,你会得到下一个"5"。
向后看:如果你从"6"向左走,下一个是"c"。如果你从"5"向左走,就是"300"。
那么原则上我该怎么做呢?我有一个方法,但它是错误的,问题已经太长了,我担心大多数人不会读它:(.我可以稍后发布。
p.S。即使很难抗拒:这个问题的答案不是"你为什么要这样做,为什么要这样组织你的数据,为什么不先[压平列表|超出我想象的东西]?问题正是我在这里描述的,没有其他问题。数据是按照问题的性质以这种方式结构化的。
一个解决方案是存储当前索引和/或深度信息,并使用它遍历嵌套列表。但这似乎是一个需要进行大量复杂分叉的解决方案——测试列表末尾等等。相反,我想出了一个折衷方案。我没有将列表列表扁平化,而是创建了一个生成器,在列表中创建一个扁平的索引列表:
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,)
然后是一个简单的函数,它基于索引元组从列表列表中提取最里面的项:
def tuple_index(nested_list, index_tuple):
for i in index_tuple:
nested_list = nested_list[i]
return nested_list
现在,您所要做的就是以任何您喜欢的方式遍历平面索引列表。
>>> 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
由于我在ideone上的评论中发布的基于堆栈的解决方案接受了这个答案,并且最好不要使用外部粘贴框作为答案代码,请注意,这个答案也包含我的基于堆栈解决方案。
我会让光标有一堆数组的索引。
示例:
[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]处。
向左移动与向右移动正好相反。
虽然我喜欢扁平化索引列表的想法,但这使得在迭代嵌套列表时无法修改任何子列表的长度。如果这不是你需要的功能,我会同意的。
否则,我还会将指向列表的指针实现为索引元组,并依赖递归。首先,这里有一个类,它实现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()
本质上,我会将自己的解决方案建立在递归的基础上。我将用以下内容扩展容器类:
cursor_position
-存储高亮显示元素(或包含高亮显示元素的元素的元素,或任何级别的递归)的索引的属性repr_with_cursor
-此方法应返回容器内容的可打印版本,该版本已高亮显示当前选定的项目mov_right
-当光标向右移动时,应调用此方法。返回元素中光标的新索引,如果光标位于当前容器的"外部"(如果移动到容器中的最后一个元素),则返回None
mov_left
-伊德姆,但向左
递归的工作方式是,对于每个方法,根据突出显示的方法的类型,您应该有两种不同的行为:
- 如果光标位于容器上,它应该调用"pointed"容器的方法
- 如果光标位于非容器上,它应该执行"实际操作"
编辑我有半个小时的空闲时间,所以我整理了一个实现我想法的示例课。它的功能并不完整(例如,当它到达最大容器的任一端时,它不能很好地处理,并且要求类的每个实例在最大序列中只使用一次),但它足以演示这个概念在人们评论之前,我会重复一遍:这是概念验证代码,它还没有准备好使用
#!/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], !]
有趣的问题!我今天最喜欢的操作系统问题!:)