はじめに
こんにちは。孔子の80代目子孫兼AS部DS2課の孔です。collectionsライブラリを活用しよう第3弾!今回はdeque
(「デック」と読みます)の紹介です。
第1弾のnamedtuple
、第2弾のOrderedDict
はそれぞれtuple
とdict
の拡張版であることは名前から推測することができますが、今回のdeque
は何の拡張版なのか名前だけ見ると明瞭ではありませんね。
deque
はlist
の拡張版となります。deque
は簡単に説明すると、「キュー + スタック」と言えます。それではキューとスタックとは何か?を先にみてみましょう!
Queue(キュー)とは?
キューは、「先入れ先出し」方式(FIFO: First In, First Out)でデータを出し入れするデータ構造です。以下の絵を見てください。
上記のようなキューがあった場合、データを取り出す順番は「1, 2, 7, 4」になり、後から投入されたデータは「4」の後ろに溜まっていき、「4」が取り出されたら次々と取り出すことができます。
(ちなみに、データをキューに入れることを「エンキュー(Enqueue)」といい、出すことを「デキュー(Dequeue)」(deque
ではありません!!)と言います。)
つまり、溜まっているデータの中で最も先に入ったデータが先に出される(先入れ先出し)構造になります。これがキューになります。Pythonではキューを標準ライブラリで提供しています。以下は簡単なキューの使い方です。
import queue q = queue.Queue(maxsize=4) # 要素が3つまで入るキューを生成する for i in range(3): q.put(i) # キューにデータを投入する q.qsize() # 3, キューに溜まった要素の数を返す q.full() # True, maxsize == q.qsize()のため for _ in range(3): q.get() # 0, 1, 2の順番で取得 q.empty() # True, キューに溜まった要素がなければTrue, なければFalse
Stack(スタック)とは?
キューが「先入れ先出し」方式だったことに対して、スタックは「後入れ先出し」方式(LIFO: Last In, First Out)でデータを出し入れします。出す順番がキューの逆だと思うと楽ですね。
一般的にLIFO方式のキューをスタックと呼びますが、PythonではLifoQueue
というデータ構造名で実装されています。Queue
同様、queue
ライブラリから使用することができます。使い方はQueue
と変わりません。
import queue q = queue.LifoQueue(maxsize=3) # 要素が3つまで入るキューを生成する for i in range(3): q.put(i) # キューにデータを投入する q.qsize() # 3, キューに溜まった要素の数を返す q.full() # True, maxsize == q.qsize()のため for _ in range(3): q.get() # 2, 1, 0の順番で取得 q.empty() # True, キューに溜まった要素がなければTrue, なければFalse
deque: Queue + Stack
それでは、deque
をみてみましょう。deque
という名前は「double-ended queue」の略語になります。キュー
の先頭、スタック
の末尾という両方の末端(double-ended)からデータを出し入れできるための名前になります。わかりやすくするため、以下の図を見てください。
両方の末端どこからもデータを投入することができ、どこからもデータを取り出すことができます。これがdeque
となります。Pythonでdeque
は以下のように利用できます。
from collections import deque dq = deque('bcd') dq # deque(['b', 'c', 'd']) dq.append('e') # 右側に'e'を追加 dq.appendleft('a') # 左側に'a'を追加 dq # deque(['a', 'b', 'c', 'd', 'e']) dq.pop() # e, 右側の要素を取得 dq.popleft() # a, 左側の要素を取得 dq.rotate(1) # deque(['d', 'b', 'c']), 右回りに要素を回転 dq.rotate(-1) # deque(['b', 'c', 'd']), 左回りに要素を回転 dq[1] # 'c', インデックス指定で値取得
基本的に、deque
で実現できる操作自体はlist
を使っても実装することができます。ただし、list
はappendleft
された際(list.insert(0, element)
)にリストに入ってる全ての要素のインデックスを貼り直す必要があり、リストのサイズが大きくなればなるほどappendleft
処理が遅くなります。
このようなパフォーマンスを考慮して実装されたのがdeque
で、deque
のサイズにほぼ影響なくappend
, appendleft
処理ができるのがdeque
のメリットとなります。
最後に
キューとスタックは基礎的なデータ構造であるため、より詳細を調べてみるのも良いかもしれません!
第3弾までtuple
, dict
, list
の拡張版となるデータ構造を紹介しました。set
は…?と思った方もいらっしゃるかと思いますが…set
はありません!
ということで、本シリーズは以上で終了となります。ありがとうございました!
参考資料
- collections
- deque
- queueモジュール
- キューとは
- スタックとは
- デックとは