2010-04-20 50 views
2

我想摺疊或展開列表的子序列摺疊或展開Python中列表的子序列的優雅方式?

例如, ['A', 'B', 'D', 'E', 'H'] -> ['AB', 'DE', 'H']反之亦然

編輯:上面的例子可能會導致誤解。以下更好:

例如['foo', 'bar', 'wtf'] <-> ['baz', 'wtf']

目前我寫了一些醜陋的代碼,如:

while True: 
    for i, x in enumerate(s): 
    if x == 'foo' and s[i+1] == 'bar': 
     s[i:i+2] = 'baz' 
     break 
    else: 
    break 

誰的人問:「爲什麼那件事」:

其實我工作的一個優化的編譯器,這是窺視孔部分。 書寫模式匹配有點煩人。

P.S.我發現下面的代碼工程,但有點可笑,爲什麼枚舉知道我們的修改?

s = ['foo', 'bar', 'wtf', 'foo', 'bar', 'wtf', 'foo', 'bar', 'wtf'] 

def collapse(): 
    for i, x in enumerate(s): 
     if s[i] == 'foo' and s[i+1] == 'bar': 
      s[i:i+2] = ['baz'] 

def expand(): 
    for i, x in enumerate(s): 
     if s[i] == 'baz': 
      s[i:i+1] = ['foo', 'bar'] 

collapse() 
print s 
expand() 
print s 
+0

「foo''和'bar''怎麼變成」baz''? – SilentGhost 2010-04-20 13:41:06

+0

找到foo,然後轉到baz – inv 2010-04-20 15:02:36

回答

1

請參閱itertools。具體而言,這裏是或多或少你想要的配方(實際上,我還以爲你的那種誤導性的原帖後想!):

from itertools import tee, izip 

def pairwise(iterable): 
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = tee(iterable) 
    next(b, None) 
    return izip(a, b) 

這將返回記錄,您可以join()

要撤銷此操作,只需join()您的最終序列並遍歷各個項目(字符)。

我會試着想出一個對你的新/真實問題的答案。

+0

對不起,我沒有得到與我的問題相關的觀點:(也許這個例子太糟糕了,我已經編輯了它,請再次檢查它 – inv 2010-04-20 13:19:43

+0

@forgot,而不是使用索引來獲取下一個元素,使用它來做自動lookahead,它仍然有點複雜,因爲你必須跳過你在下一步添加的內容,但這就是生活,你必須添加很多併發症,使你目前的方法正確無誤地工作 – 2010-04-20 15:38:34

+0

@Mike格雷厄姆,絕對,悲傷,但真實:) – inv 2010-04-20 23:03:48

0

我認爲你的枚舉方式實際上是相當不錯的。枚舉可以跟蹤修改,因爲它創建了一個使用您輸入的數組的迭代器的生成器。如果你改變你的陣列是我看到的問題是:

s = ['foo', 'bar', 'wtf', 'foo', 'bar', 'wtf', 'foo'] 

則最後的「富」不擁有「酒吧」會當你的代碼嘗試看看超越項目給你一個例外數組的末尾。我不知道如何解決這個問題,因爲我的嘗試沒有成功。

編輯:

它可能不是很優雅,但此代碼爲collapse()功能將運行,即使在上述情況下:

def collapse(): 
    i = 1 
    L = len(s) 
    while i < L: 
     if s[i-1] == 'foo' and s[i] == 'bar': 
      s[i-1:i+1] = ['baz'] 
      L -= 1 
     i += 1 
+0

人們當然有權利downvote我的答案,但爲什麼我的答案如此糟糕? – 2010-04-20 18:01:50

+0

我還沒有投票。你的回答並不差,但似乎python編碼人員不喜歡這樣保持索引。順便說一句,你可以簡單地用'while len'代替變量L:' – inv 2010-04-20 23:02:28

+0

@forgot是的,你可以替換,如果你想。我首先想到了這樣做,但我很確定len()函數會慢一點。如果你打算將它用作編譯器,我認爲速度可能很重要。但是,我承認這是過早的優化(這是邪惡的)。 – 2010-04-20 23:36:05

2

我不會把這個好得多,但它是一個不同的方式來做到這一點,它也處理賈斯廷指出的怪癖。(我更感興趣的是從列表中找到一個子,我無法找到谷歌一個很好的功能)

def findsubseq(L, subseq): 
    if not subseq: return # just die on zero-len input 
    i = -1 
    try: 
     while True: 
      i = L.index(subseq[0], i+1) 
      for j in range(1, len(subseq)): 
       if L[i+j] != subseq[j]: 
        break 
      else: 
       yield i 
    except ValueError: pass 
    except IndexError: pass 

def replace(target, changethis, tothis): 
    subseqs = [x for x in findsubseq(target, changethis)] 
    subseqs.reverse() 
    for i in subseqs: 
     target[i:i+len(changethis)] = tothis 
def collapse(): 
    global s 
    replace(s, ['foo', 'bar'], ['baz']) 
def expand(): 
    global s 
    replace(s, ['baz'], ['foo', 'bar']) 

s = ['foo', 'bar', 'wtf', 'foo', 'bar', 'wtf', 
     'foo', 'bar', 'bar', 'bar', 'foo'] 
print s 
collapse() 
print s 
expand() 
print s 

C:\Scripts>subseq.py 
['foo', 'bar', 'wtf', 'foo', 'bar', 'wtf', 'foo', 'bar', 'bar', 'bar', 'foo'] 
['baz', 'wtf', 'baz', 'wtf', 'baz', 'bar', 'bar', 'foo'] 
['foo', 'bar', 'wtf', 'foo', 'bar', 'wtf', 'foo', 'bar', 'bar', 'bar', 'foo'] 

編輯:廣義它只是一個簡單的替換功能

+0

我想知道這是否可以重新焊接,以便發電機可以提供指示來代替它,而不是1)獲取所有信息,2)翻轉它們以避免收縮/增長將它們扔掉,3)替換,但發電機可能需要調整搜索的位置('list.index()')如果'changethis'爲空,這可能導致'thisthis == tothis'導致它掛起。很容易修復,但這是作爲練習給讀者的。 – 2010-04-20 18:41:11