4

このリストを仮定すると

nestedList = ["a", "b", [1, 2, 3], "c",[4, 5, 6, [100, 200, 300]], "d"]

任意の深さのネストされたリストの位置リストを返す関数があります。 :

[2, 1] -> "2"
[5] -> "d"
[4, 3, 2] -> "300"

ご覧のとおり、ネストのレベル数は最初は明確ではありません。

追加の問題 リストの変更に [:] または [4:] または [0:1] 表記を使用したい。

人間の場合は非常に簡単です。必要な数のインデックス位置を追加するだけです。

nestedList[2][1]
nestedList[5]
nestedList[4][3][2]
nestedList[4][1:] = NewItem + nestedList[4][1:] #insert item
nestedList[2][1] = [] #remove item

ただし、文字列を一緒に追加して後で評価する必要があったため、このアプローチはどこにもつながりません。明らかなナンセンス:)

未知の数のインデックス位置を持つネストされたリストを処理し、通常のリストのように処理する機能 (読み取り、変更、挿入、削除) を保持する最良の方法は何ですか?

それに対する答えがあることを願っています。

PSリストはネストされたままにする必要があります。フラット化はオプションではありません。

4

2 に答える 2

7

最初の部分は簡単です。

>>> reduce(lambda x, y: x[y], [4, 3, 2], nestedList)
300

2 番目の部分はさらに多くの労力を必要としますが、それでも実行可能です。ヒント:

>>> a = [1, 2, 3]
>>> a[slice(1, None)] = [4, 5]
>>> a
[1, 4, 5]
于 2011-07-02T17:05:44.810 に答える
1

私はついにこれをいじる時間ができました。少し夢中になりました。長いですが、とりあえず貼っておきます。、、、、およびメソッドと、カーソルの抽象化を破る低レベルの操作を可能にするいくつかのプライベート メソッドを追加set_itemしました。また、範囲外または非トップレベル オブジェクトを指す for インデックス タプルをスローするメソッドも追加しました。insertdeletefindfind_leftmove_cursorIndexError

基本的に、パブリック関数のみを使用する限り、カーソルは常にトップレベルのオブジェクトを指し、挿入と削除はすべてトップレベルで行われることが (保証されるべき) です。__getitem__ここから、__setitem____delitem__、 などを安全に実装できるはず__getslice__です__setslice__

ただし、いくつかのしわがあります。カーソルが常にトップレベルのオブジェクトを指すという制限により、ネストされたリストをフラット リストであるかのように反復処理することが非常に簡単になります。insertただし、カーソルが下位レベルのオブジェクトを指すことができないことも意味するため、単独では使用できない種類の挿入があります。たとえば、次の 3 つのリストがあるとします。

>>> l1 = [1, 2, 3, 4]
>>> l2 = [5, 6, 7, 8]
>>> l3 = [l1, l2]
>>> l3
[[1, 2, 3, 4], [5, 6, 7, 8]]

このネストされた構造を NLI に入れ、 に移動して5、挿入を試みます。

>>> nli = NestedListIter(l3)
>>> nli.find(5)
>>> nli.insert(9)
>>> nli.nested_list
[[1, 2, 3, 4], [9, 5, 6, 7, 8]]

ご覧のとおり、 に何かを挿入することはできl2ますが、 に何かを簡単に挿入することはできませんl3。実際、今すぐそうするには、private 関数を使用する必要があります。これは、カーソルの抽象化を不愉快な方法で壊します。

>>> nli._insert_at(nli.stack[:-1], 10)
>>> nli.nested_list
[[1, 2, 3, 4], 10, [9, 5, 6, 7, 8]]
>>> nli.get_item()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "nestedlistiterator.py", line 130, in get_item
    return self._get_item_at(self.stack)
  File "nestedlistiterator.py", line 39, in _get_item_at
    item = item[i]
TypeError: 'int' object is unsubscriptable

安全なパブリック メソッドを実装する方法は確かに存在しますがinsert_between_branches、それらの方法には、今私が気にかけている以上に複雑さが伴います。

の後に値を挿入しようとすると、別の問題が発生します4l2これまで見てきたように、 beforeに値を挿入できます5が、カーソルを4andに移動すると、 inside のinsert後に何かを挿入できないことがすぐにわかります。4l1

>>> nli.go_to_head()
>>> nli.find(4)
>>> nli.insert(11)
>>> nli.nested_list
[[1, 2, 3, 11, 4], 10, [9, 5, 6, 7, 8]]

フラット アクセスの観点からは、4 の後に挿入することと 5 の前に挿入することは同じことですが、ネストされたリストの観点からは異なります。insertは事実上 であるため、left_insertこの問題はメソッドで部分的に修正できますright_insert(その結果、l1 の先頭に挿入できなくなります)。

これらの問題は、カーソルが下位レベルのオブジェクトを指すようにすることで、より一般的に対処できる可能性がありますが、フラット アクセスがより複雑になります。要するに、これらの問題を修正しようとすると、インターフェイスのフラット側またはネストされた側のいずれかで複雑さが増します。

(これが、私がまだ単純な方法を好む理由enumerate_nestedです! すべてのノード (最上位ノードだけでなく) に値を持つ適切なツリー構造も、より単純で優れている可能性があります。それでも、これはコーディングするのが楽しかったです。)

import collections

class NestedListIter(object):
    '''A mutable container that enables flat traversal of a nested tree of 
    lists. nested_list should contain only a list-like mutable sequence. 
    To preserve a clear demarcation between 'leaves' and 'branches', empty 
    sequences are not allowed as toplevel objects.'''
    def __init__(self, nested_list):
        if not nested_list:
            raise ValueError, 'nested_list must be a non-empty sequence'
        self.nested_list = nested_list # at some point, vet this to make sure
        self.go_to_head()              # it contains no empty sequences

    def _is_sequence(self, item=None):
        '''Private method to test whether an item is a non-string sequence.
        If item is None, test current item.'''
        if item is None:
            item = self._get_item_at(self.stack)
        return isinstance(item, collections.Sequence) and not isinstance(item, basestring)

    def _is_in_range(self, index_tuple=None):
        '''Private method to test whether an index is in range. 
        If index is None, test current index.'''
        if index_tuple is None:
            index_tuple = self.stack
        if any(x < 0 for x in index_tuple):
            return False
        try:
            self._get_item_at(index_tuple)
        except IndexError:
            return False
        else:
            return True

    def _get_item_at(self, index_tuple):
        '''Private method to get item at an arbitrary index, with no bounds checking.'''
        item = self.nested_list
        for i in index_tuple:
            item = item[i]
        return item

    def _set_item_at(self, index_tuple, value):
        '''Private method to set item at an arbitrary index, with no bounds checking.
        Throws a ValueError if value is an empty non-string sequence.'''
        if self._is_sequence(value) and not value:
            raise ValueError, "Cannot set an empty list!"
        containing_list = self._get_item_at(index_tuple[:-1])
        containing_list[index_tuple[-1]] = value

    def _insert_at(self, index_tuple, value):
        '''Private method to insert item at an arbitrary index, with no bounds checking.
        Throws a ValueError if value is an empty non-string sequence.'''
        if self._is_sequence(value) and not value:
            raise ValueError, "Cannot insert an empty list!"
        containing_list = self._get_item_at(index_tuple[:-1])
        containing_list.insert(index_tuple[-1], value)

    def _delete_at(self, index_tuple):
        '''Private method to delete item at an arbitrary index, with no bounds checking.
        Recursively deletes a resulting branch of empty lists.'''
        containing_list = self._get_item_at(index_tuple[:-1])
        del containing_list[index_tuple[-1]]
        if not self._get_item_at(index_tuple[:-1]):
            self._delete_at(index_tuple[:-1])

    def _increment_stack(self):
        '''Private method that tires to increment the top value of the stack.
        Returns True on success, False on failure (empty stack).'''
        try:
            self.stack[-1] += 1
        except IndexError:
            return False
        else: 
            return True

    def _decrement_stack(self):
        '''Private method that tries to decrement the top value of the stack.
        Returns True on success, False on failure (empty stack).'''
        try:
            self.stack[-1] -= 1
        except IndexError:
            return False
        else:
            return True

    def go_to_head(self):
        '''Move the cursor to the head of the nested list.'''
        self.stack = []
        while self._is_sequence():
            self.stack.append(0)

    def go_to_tail(self):
        self.stack = []
        '''Move the cursor to the tail of the nested list.'''
        while self._is_sequence():
            self.stack.append(len(self.get_item()) - 1)

    def right(self):
        '''Move cursor one step right in the nested list.'''
        while self._increment_stack() and not self._is_in_range():
            self.stack.pop()
        if not self.stack:
            self.go_to_tail()
            return False
        while self._is_sequence():
            self.stack.append(0)
        return True

    def left(self):
        '''Move cursor one step left in the nested list.'''
        while self._decrement_stack() and not self._is_in_range():
            self.stack.pop()
        if not self.stack:
            self.go_to_head()
            return False
        while self._is_sequence():
            self.stack.append(len(self.get_item()) - 1)
        return True

    def move_cursor(self, index_tuple):
        '''Move cursor to the location indicated by index_tuple.
        Raises an error if index_tuple is out of range or doesn't correspond
        to a toplevel object.'''
        item = self._get_item_at(index_tuple)
        if self._is_sequence(item):
            raise IndexError, 'index_tuple must point to a toplevel object'

    def get_item(self):
        '''Get the item at the cursor location.'''
        return self._get_item_at(self.stack)

    def set_item(self, value):
        '''Set the item a the cursor locaiton.'''
        return self._set_item_at(self.stack, value)

    def insert(self, value):
        '''Insert an item at the cursor location. If value is a sequence, 
        cursor moves to the first toplevel object in value after insertion. 
        Otherwise, cursor does not move.'''
        temp_stack = self.stack[:]
        self.left()
        self._insert_at(temp_stack, value)
        self.right()

    def delete(self):
        '''Deete an item at the cursor location. Cursor does not move.'''
        temp_stack = self.stack[:]
        self.left()
        self._delete_at(temp_stack)
        self.right()

    def iterate(self):
        '''Iterate over the values in nested_list in sequence'''
        self.go_to_head()
        yield self.get_item()
        while self.right():
            yield self.get_item()

    def iterate_left(self):
        '''Iterate over the values in nested_list in reverse.'''
        self.go_to_tail()
        yield self.get_item()
        while self.left():
            yield self.get_item()

    def find(self, value):
        '''Search for value in nested_list; move cursor to first location of value.'''
        for i in self.iterate():
            if i == value:
                break

    def find_left(self, value):
        '''Search for value backwards in nested_list; move cursor to last location of value.'''
        for i in self.iterate_left():
            if i == value:
                break

def _NLI_Test():
    l = [1, 2, 3, ['a', 'b', 'c'], 4, ['d', 'e', [100, 200, 300]], 5, ['a', 'b', 'c'], 6]
    nli = NestedListIter(l)
    print nli.nested_list
    for i in nli.iterate():
        print i,
    print
    for i in nli.iterate_left():
        print i,
    print

    nli.go_to_head()
    for i in range(5):
        nli.right()
    nli.insert('cow')
    nli.insert(['c', ['o', 'w']])
    print nli.nested_list
    nli.find('cow')
    print nli.get_item()
    nli.delete()
    print nli.nested_list
    nli.find('c')
    nli.delete()
    print nli.nested_list
    nli.find_left('w')
    nli.delete()
    nli.find('o')
    nli.delete()
    print nli.nested_list
    print nli.nested_list == l
    nli.find(100)
    nli.set_item(100.1)
    print nli.nested_list

if __name__ == '__main__':
    _NLI_Test()
于 2011-07-07T23:07:29.563 に答える