5

私はPythonで書いていますが、抽象的な概念は私や他の人にとってもっと興味深いと思います。したがって、必要に応じて擬似コードを使用してください:)

あるクラスのアイテムのリストがあります。ここで文字列と数字を使ってやってみましょう、それは本当に問題ではありません。任意の深さにネストされています。(実際にはリストではなく、リストに基づくコンテナクラスです。)

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

['a'、'b'、'c']は両方とも実際には同じコンテナであることに注意してください。一方を変更すると、もう一方も変更されます。コンテナとアイテムは編集でき、アイテムは挿入され、最も重要なコンテナは複数回使用できます。冗長性を回避するために、1つのコンテナーにアイテムを挿入する機能が失われ、他のすべてのコンテナーに自動的に表示されるため、リストをフラット化することはできません(私は思います!)。

問題:フロントエンドの場合(python "cmd"モジュールを使用したコマンドラインのみ)、読み取りまたは編集できるように常に現在のアイテムを指すカーソルを使用して、この構造をナビゲートしたいと思います。カーソルは左右に移動でき(ユーザーの視点)、リストがネストされたリストではなくフラットなリストであるかのように動作する必要があります。

人間にとって、これは非常に簡単です。上記のこのリストにはサブリストが存在しないふりをして、左から右へ、そして後ろへと移動します。

たとえば、上記のリストの「3」の位置にいて右に行くと、次の項目として「a」、次に「b」、「c」、次に「4」などが表示されます。 「300」は次に「5」を取得します。

そして後方:「6」から左に行くと、次は「c」です。「5」から左に行くと「300」になります。

では、原則としてそれをどのように行うのでしょうか?私はここに1つのアプローチがありますが、それは間違っており、質問はすでに非常に長いため、ほとんどの人がそれを読まないのではないかと心配しています:(。後で投稿できます。

PS抵抗するのが難しい場合でも、この質問に対する答えは、「なぜこれを実行したいのか、なぜこのようにデータを整理するのか、最初に[リストをフラット化|私の想像から何か]しないのか」ではありません。 ?問題は私がここで説明したものとまったく同じであり、他には何もありません。データはこのように問題の性質によって構造化されています。

4

4 に答える 4

3

1つの解決策は、現在のインデックスや深度情報を保存し、それを使用してネストされたリストをトラバースすることです。しかし、それは多くの複雑なフォークを行うソリューションのように思えます-リストの終わりのテストなど。代わりに、私は妥協案を思いつきました。リストのリストをフラット化する代わりに、インデックスのフラットなリストをリストのリストに作成するジェネレーターを作成しました。

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に投稿したスタックベースのソリューションで受け入れられ、回答コードに外部のペーストビンを使用しないことが望ましいため、この回答にはスタックベースのソリューションも含まれていることに注意してください。

于 2011-07-01T14:41:25.440 に答える
3

カーソルに配列のインデックスのスタックを持たせます。

例:

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

カーソルが1(インデックス0)にある場合、カーソルの位置は[0]です。

カーソルが2(インデックス1)にある場合、カーソルの位置は[1]です。

カーソルが「a」(最外レベルのインデックス3、2番目のレベルのインデックス0)にある場合、カーソルの位置は[3、0]になります。

カーソルが「b」(最外レベルのインデックス3、2番目のレベルのインデックス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-同じですが、左側にあります。

再帰が機能する方法は、各メソッドについて、強調表示されたメソッドのタイプに応じて、2つの異なる動作が必要になることです。

  • カーソルがコンテナ上にある場合は、「指定された」コンテナのメソッドを呼び出す必要があります。
  • カーソルがコンテナ以外にある場合は、「本物」を実行する必要があります。

編集 私は30分余裕があったので、自分のアイデアを実装するサンプルクラスをまとめました。機能が完全ではありません(たとえば、最大のコンテナのいずれかの端に到達するとうまく処理されず、クラスの各インスタンスを最大のシーケンスで1回だけ使用する必要があります)が、概念を示すには十分に機能します。人々がそれについてコメントする前に繰り返します:これは概念実証コードであり、使用する準備ができていません!

#!/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], !]

楽しい問題!その日の私のお気に入りのOSの質問!:)

于 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 に答える