Python Cookbook 之一 數據結構和算法(三): 保存最後 N 個元素

一、問題

我們希望在迭代或是其他形式的處理過程中對最後幾項記錄做一個有限的歷史記錄統計。

二、解決方案

保存有限的歷史記錄可算是 collections.deque 的完美應用場景了。

如: 想要對一系列文本行做簡單的文本匹配操作, 當發現有匹配時就輸出當前的匹配行以及最後檢查過的 N 行文本:

from collections import deque


def find_value(lines, pattern, length=5):
    previous_lines = deque(maxlen=length)
    for line in lines:
        if pattern in line:
            yield line, previous_lines
        previous_lines.append(line)


if __name__ == '__main__':
    with open('test.txt') as f:
        for line, prevlines in find_value(f, 'python', 5):
            for i in prevlines:
                print('11  ' + i, end='')
            print('  22  ' + line, end='')
            print('-'*20)
            print()

其中, test.txt 文本文件內容如下:

I love python.
You love java.
python and java.
php
c and c++
ruby
deeplearning
python
python and c
c++ and python
I use python.

運行結果如下所示:

  22  I love python.
--------------------

11  I love python.
11  You love java.
  22  python and java.
--------------------

11  python and java.
11  php
11  c and c++
11  ruby
11  deeplearning
  22  python
--------------------

11  php
11  c and c++
11  ruby
11  deeplearning
11  python
  22  python and c
--------------------

11  c and c++
11  ruby
11  deeplearning
11  python
11  python and c
  22  c++ and python
--------------------

11  ruby
11  deeplearning
11  python
11  python and c
11  c++ and python
  22  I use python.
--------------------

三、討論

當編寫搜索某項記錄的代碼時, 通常會用到含有 yield 關鍵字的生成器函數。 因爲這會將處理搜索過程的代碼和使用搜索結果的代碼成功解耦開來。

deque(maxlen=N) 創建了一個固定長度的隊列。 當有新記錄加入而隊列已滿時會自動移除最老的那條記錄。

>>> from collections import deque
>>> d = deque(maxlen=4)
>>> d.append(1)
>>> d.append(2)
>>> d.append(3)
>>> d.append(4)
>>> d
deque([1, 2, 3, 4], maxlen=4)
>>> d.append(5)
>>> d
deque([2, 3, 4, 5], maxlen=4)

儘管可以在列表上手動完成這樣的操作(append、 del), 但隊列這種解決方案要優雅的多, 而且 運行速度也會快很多

如果不指定隊列的大小, 也就得到了一個無界限的隊列, 可以在 兩端 執行添加和彈出的操作:

>>> from collections import deque
>>> d = deque()
>>> d.append(1)
>>> d.append(2)
>>> d.append(3)
>>> d
deque([1, 2, 3])
>>> d.appendleft(4)
>>> d
deque([4, 1, 2, 3])
>>> d.popleft()
4
>>> d
deque([1, 2, 3])
>>> d.append(5)
>>> d
deque([1, 2, 3, 5])
>>> d.appendleft(6)
>>> d
deque([6, 1, 2, 3, 5])
>>> d.popleft()
6

【注意】從隊列兩端添加或彈出元素的複雜度都是 O(1)。 這和列表不同, 當從列表的頭部插入或移除元素時, 列表的複雜度爲 O(N)。這也是爲什麼使用隊列會更快的原因!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章