課題06-1 Stack#
LIFO(Last in, First out; 後入れ先出し)のStackを表すクラスを実装してください。
pushメソッドにitemを渡すと、リストの一番最後にitemを追加します。popメソッドを呼び出すと、リストの一番最後の要素を取り出して返します(リストからは最後の要素を削除します)。 要素が存在しない場合(リストの長さが0の場合)はNoneを返します。peekメソッドは、リストの一番最後の要素を返しますが、リストからは要素を削除しません。lenメソッドは、スタックにある要素数を返します。
なお、コンストラクタの引数でリストを初期化できるようにしてください(デフォルト引数で None を受け取るようにしてください。理由はこちら)。
Stack クラスの定義の下に以下の Python コードを書いて実行してテストが通るように stack.py を作成してください。
class Stack:
pass # pass を削除して、以下のテストコードが通るようプログラムを書いてください
s1 = Stack()
s1.push("This")
s1.push("is")
s1.push("stack")
s1.push("test")
assert s1.len() == 4, "スタックの長さは4になっているはずです"
assert s1.peek() == "test", "peek() は最後の要素である test を返すべきです"
assert s1.len() == 4, "peek() は要素の長さを変更しません"
assert s1.pop() == "test", "pop() は最後の要素である test を返すべきです"
assert s1.pop() == "stack", "pop() は最後の要素である stack を返すべきです"
assert s1.pop() == "is", "pop() は最後の要素である is を返すべきです"
assert s1.pop() == "This", "pop() は最後の要素である This を返すべきです"
assert s1.len() == 0, "スタックの長さは0になっているはずです"
s2 = Stack([1, 2, 3, 4, 5]) # コンストラクタで要素を指定できる。
assert s2.len() == 5, "スタックの長さは5になっているはずです"
s2.pop()
s2.pop()
s2.pop()
s2.pop()
s2.pop()
assert s2.len() == 0, "スタックの長さは0になっているはずです"
s3 = Stack()
assert s3.pop() == None, "空のスタックから pop() すると None が返るべきです"課題06-2 環状バッファ#
n個の要素を持つ環状バッファを表すRingBufferクラスを実装してください。
環状バッファとは、n 個の要素を持つリング状のバッファです。
要素を追加したときnを超えると、最初に追加した要素が上書きされる構造です。
コンストラクタで n を受け取ってください。ただし、nが指定されない場合は 12 が指定されたものとしてください。
RingBuffer クラスの定義の下に以下の Python コードを書き、実行するとエラーなく実行できるようにしてください。
ring_buffer.py というファイル名で保存してください。
RingBuffer クラスが持つべきメソッドは以下のテストコードを参照の上、考えてみてください。
class RingBuffer:
pass # pass を削除して、以下のテストコードが通るようプログラムを書いてください
rb1 = RingBuffer(3)
assert rb1.get(0) == None, "何も要素が入っていないため、None であるはずです"
rb1.add(1)
assert rb1.get(0) == 1, "0番目の要素は 1 であるはずです"
rb1.add(2)
rb1.add(3)
rb1.add(4) # 最初の要素である 1 が上書きされる。
assert rb1.get(0) == 4, "0番目の要素は 4 であるはずです"
assert rb1.get(1) == 2, "0番目の要素は 2 であるはずです"
assert rb1.get(2) == 3, "0番目の要素は 3 であるはずです"
assert rb1.len() == 3, "3つの要素が入っているため、長さは3であるはずです"
rb2 = RingBuffer()
assert rb2.get(0) == None, "何も要素が入っていないため、None であるはずです"
rb2.add("item1")
rb2.add("item2")
assert rb2.len() == 2, "2つの要素が入っているため、長さは2であるはずです"
assert rb2.get(5) == None, "5番目の要素は存在しないため、None であるはずです"以下の図は、上記の rb1、rb2 の動作イメージです。クリックすると更新されます。

課題06-3 拡張子ごとのワードカウント#
指定されたディレクトリ以下のファイルを再帰的に検索し、拡張子ごとに文字数、行数、ファイル数を数えるプログラムを作成してください。
そして、拡張子ごとに文字数、行数、ファイル数を表示してください。
word_count.py というファイル名で保存してください。
WordCountsはself.typesというディクショナリをもち、拡張子をキーとして、Countsオブジェクトを値として持ちます。Countsオブジェクトは、拡張子ごとの文字数、行数、ファイル数を保持します。Countsオブジェクトは、最初に拡張子が見つかったときに、初期化されます。- また、
Countsオブジェクトのparseメソッドは、当該拡張子を持つファイルが見つかった時に呼び出されます。- そのため、
parseメソッドが呼び出されると、ファイル数を+1し、ファイルの内容を読み込んで文字数と行数を数えて、self.linesとself.charsに加算します。
- そのため、
import os
import sys
class Counts:
def __init__(self):
# 文字数(chars)、行数(lines)、ファイル数(files)を初期化する。
def parse(self, file):
with open(file, 'r') as f:
# ファイルの内容を読み込んで、文字数と行数を数える。
# ファイル数を1増やす。
def str(self):
return f"files: {self.files:5}, chars: {self.chars:5}, lines: {self.lines:5}"
class WordCounts:
def __init__(self):
self.types = {}
def traverse(self, path):
if os.path.isdir(path): # ディレクトリかどうかを判定する。
for p in os.listdir(path): # ディレクトリ内のファイル、ディレクトリを列挙する。
new_path = os.path.join(path, p) # ディレクトリ名とファイル名を結合する。
self.traverse(new_path) # ディレクトリを再帰的に探索する。
else: # ディレクトリではない場合(ファイルの場合)
ext = # 拡張子を取得する(_find_ext を呼び出す)。
if ext not in self.types: # ディクショナリに拡張子に対応する Counts が存在するか。
pass # 存在しなければ、新たに作成して、types に追加する。
self.types[ext].parse(path) # ファイルを解析する。
def _find_ext(self, path): # ファイル名を受け取り、拡張子を返す。
ext = os.path.splitext(path)[-1] # 拡張子を取得する(一番最後の要素を取得する)。
if ext == '': # 拡張子が存在しない場合
return os.path.basename(path) # ファイル名を返す。
return ext # 拡張子を返す。
def print(self):
for ext, counts in self.types.items(): # 拡張子と Counts のペアを取り出す。
print(f"{ext:12}: {counts.str()}") # 結果を出力する。
if os.path.isdir("helloworld"): # helloworld.zip を展開した helloworld がある場合だけテストする。
test = WordCounts()
test.traverse("helloworld")
assert test.types[".sh"].files == 2, ".sh のファイルは 2 つ"
assert test.types[".sh"].lines == 29, ".sh の行数は合計 29"
assert test.types[".sh"].chars == 829, ".sh の文字数は合計 829"
assert test.types[".go"].files == 1 and test.types[".go"].lines == 20
assert "LICENSE" in test.types, "拡張子のないファイルはファイル名がキーになる"
wc = WordCounts()
for arg in sys.argv[1:]:
wc.traverse(arg)
wc.print()実行例#
以下の helloworld.zip をダウンロードして展開してできたディレクトリ、helloworld を指定すると以下のような結果になります。
$ python3 word_count.py helloworld
.sh files: 2, chars: 829, lines: 29
.mod files: 1, chars: 45, lines: 3
LICENSE files: 1, chars: 7048, lines: 121
Dockerfile files: 1, chars: 236, lines: 11
.md files: 1, chars: 565, lines: 14
.gitignore files: 1, chars: 4336, lines: 194
.go files: 1, chars: 262, lines: 20課題06-4 Queue#
FIFO(First in, First out; 先入れ先出し)の Queue を表すクラスを実装してください。
課題06-1 の Stack と作り方はほとんど同じで、取り出す側が違うだけです。
enqueueメソッドにitemを渡すと、リストの一番最後にitemを追加します。dequeueメソッドを呼び出すと、リストの 一番最初 の要素を取り出して返します(リストからはその要素を削除します)。 要素が存在しない場合はNoneを返します。peekメソッドは、リストの一番最初の要素を返しますが、リストからは要素を削除しません。lenメソッドは、キューにある要素数を返します。
Stack と同じく、コンストラクタの引数でリストを初期化できるようにしてください。
Queue クラスの定義の下に以下の Python コードを書いて実行してテストが通るように my_queue.py を作成してください(queue.py という名前は Python に元からあるモジュールと同じなので避けます)。
class Queue:
pass # pass を削除して、以下のテストコードが通るようプログラムを書いてください
q1 = Queue()
q1.enqueue("This")
q1.enqueue("is")
q1.enqueue("queue")
q1.enqueue("test")
assert q1.len() == 4, "キューの長さは4になっているはずです"
assert q1.peek() == "This", "peek() は最初の要素である This を返すべきです"
assert q1.len() == 4, "peek() は要素の長さを変更しません"
assert q1.dequeue() == "This", "dequeue() は最初の要素である This を返すべきです"
assert q1.dequeue() == "is", "dequeue() は次の要素である is を返すべきです"
assert q1.dequeue() == "queue", "dequeue() は次の要素である queue を返すべきです"
assert q1.dequeue() == "test", "dequeue() は次の要素である test を返すべきです"
assert q1.len() == 0, "キューの長さは0になっているはずです"
q2 = Queue([1, 2, 3]) # コンストラクタで要素を指定できる。
assert q2.dequeue() == 1, "最初に入れた 1 が最初に出てくるべきです"
q2.enqueue(4)
assert q2.peek() == 2, "次に出てくるのは 2 のはずです"
assert q2.len() == 3, "キューの長さは3になっているはずです"
q3 = Queue()
assert q3.dequeue() == None, "空のキューから dequeue() すると None が返るべきです"
assert q3.peek() == None, "空のキューを peek() すると None が返るべきです"