トラック
/
Python
Python
/
演習
/
単純な連結リスト
単純な連結リスト

単純な連結リスト

初級

はじめに

音楽ストリーミングサービスを提供する会社で働いています。

音楽プレイヤーアプリにプレイリスト機能を作る仕事を任されました。

説明

音楽プレイヤーアプリケーションのプロトタイプを書いてみましょう。

プロトタイプでは、それぞれの曲を単純に数値で表します。数値の範囲(曲のID)が与えられたら、単方向連結リストを作成しましょう。

単方向連結リストが与えられたら、そのリストを逆順にして、曲を反対の順番で再生できるようにしましょう。

Note

連結リストはコンピューターサイエンスにおける基本的なデータ構造で、ほかのデータ構造を実装するときによく使われます。

もっとも単純な連結リストは、単方向連結リストです。つまり、各要素(「ノード」)はデータを持ち、さらにリスト内の次のノードを指すものを持ちます。

連結リストについてもっと深く知りたい場合は、こちらの記事で、わかりやすい図を使って説明されています。

Pythonでのこの演習の構成

stacksやqueuesはlists、collections.deque、queue.LifoQueue、multiprocessing.Queueを使って実装できますが、この演習では、自作の単方向連結リストを使った「後入れ先出し」(LIFO)スタックを想定しています。


連結リストで実装されたスタックを表す図。破線の枠が付いたNew_Nodeという円が一番左側にあり、そこから右向きに2本の点線の矢印が伸びています。New_Nodeには「(becomes head) - New_Node - next = node_6」と書かれています。上の点線の矢印には「push」というラベルが付いていて、右上のNode_6を指しています。Node_6には「(current) head - Node_6 - next = node_5」と書かれています。下の点線の矢印には「pop」というラベルが付いていて、「gets removed on pop()」と書かれたボックスを指しています。Node_6からは実線の矢印が右向きに伸びてNode_5を指しています。Node_5には「Node_5 - next = node_4」と書かれています。Node_5からも実線の矢印が右向きに伸びてNode_4を指しています。Node_4には「Node_4 - next = node_3」と書かれています。このパターンはNode_1まで続き、Node_1には「(current) tail - Node_1 - next = None」と書かれています。Node_1からは点線の矢印が右向きに伸びて、「None」と書かれたノードを指しています。


これは、内部でlistやqueue、arrayを使うかもしれない動的配列やリストを使ったLIFOスタックと混同しないでください。動的配列ベースのstacksは、headの位置が異なり、時間計算量(Big-O)やメモリ使用量も異なります。


配列または動的配列で実装されたスタックを表す図。破線の枠が付いたNew_Nodeというボックスが一番右側にあり、そこから左向きに2本の点線の矢印が伸びています。New_Nodeには「(becomes head) - New_Node」と書かれています。上の点線の矢印には「append」というラベルが付いていて、左上のNode_6を指しています。Node_6には「(current) head - Node_6」と書かれています。下の点線の矢印には「pop」というラベルが付いていて、点線の輪郭を持つ「gets removed on pop()」と書かれたボックスを指しています。Node_6からは実線の矢印が左向きに伸びてNode_5を指しています。Node_5からも実線の矢印が左向きに伸びてNode_4を指しています。このパターンはNode_1まで続き、Node_1には「(current) tail - Node_1」と書かれています。


これらの考慮点については、Stack Overflowの次の2つの質問を参照してください:配列ベースとリストベースのスタックとキューと、配列スタック、連結スタック、スタックの違い。 Pythonにおける連結リスト、LIFOスタック、その他の抽象データ型(ADT)の詳細については、次の資料をご覧ください:


Pythonのクラス

Pythonで連結リストを「標準的」に実装するには、通常1つ以上のclassesが必要です。 classesのよい入門としては、classesとその関連演習であるellens-alien-game、あるいはPython公式チュートリアルのクラスの節をご覧ください。


Pythonの特殊メソッド

この演習のテストでは、LinkedListに対してlen()を呼び出します。 len()を機能させるには、__len__という特殊メソッドを作成する必要があります。 Pythonで特殊メソッドや"dunder"メソッドを実装する方法の詳細については、Pythonドキュメント: 基本的なオブジェクトのカスタマイズとPythonドキュメント: object.len(self)をご覧ください。


イテレーターを作る

LinkedListをループでたどったり逆順にしたりできるようにするには、__iter__特殊メソッドを実装する必要があります。 実装の詳細については、クラスにイテレーターを実装する方法をご覧ください。


例外のカスタマイズと送出

コードの中で例外をカスタマイズしたりraiseしたりする必要がある場合があります。 その際は、エラーの原因を示す意味のあるエラーメッセージを必ず含めてください。 そうするとコードが読みやすくなり、デバッグにも大いに役立ちます。

カスタム例外は、新しい例外クラスを作ることで作成できます(詳しくはclassesをご覧ください)。こうしたクラスは通常、Exceptionのサブクラスです。

エラーの原因が特定の例外_型_から派生したものであるとわかっている場合は、_Exception_クラスの下にあるbuilt in error typesのいずれかを継承するという選択肢があります。 エラーを送出するときも、意味のあるメッセージを含めるようにしてください。

この演習では、連結リストが空のときに送出される("thrown")_カスタム例外_を作成する必要があります。 テストに合格するには、適切な例外をカスタマイズし、その例外をraiseし、適切なエラーメッセージを含める必要があります。

汎用的な_例外_をカスタマイズするには、Exceptionを継承したclassを作成します。 メッセージ付きでカスタム例外を送出するときは、メッセージをexception型の引数として書きます:

# subclassing Exception to create EmptyListException
class EmptyListException(Exception):
    """Exception raised when the linked list is empty.

    message: explanation of the error.

    """
    def __init__(self, message):
        self.message = message

# raising an EmptyListException
raise EmptyListException("The list is empty.")
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Python Exercism

単純な連結リストを始める準備はできましたか?

Exercismに登録すれば、17個のコンセプト146個の演習、そして本物の人間によるメンタリングとともに、Pythonを学んでマスターできます。すべて無料です。