今回は小ネタでPythonでリストの値を往復させる方法について考えてみました。
例えば[1,2,3,4]
というようなリストがあった時に、指定した数だけ1,2,3,4,3,2...
という風に表示させる実装です。
まず、シンプルな実装としては、配列の長さとインデックスを比較して、インデックスを足すのか減らすのか判断する方法でしょうか。
以下のように書いてみました。
def gen_round_trip(a_list, count):
# indexを正負どちらに向けるかフラグ
reverse_flg = False
index = 0
length = len(a_list)
for i in range(count):
# 値を取り出す
ret = a_list[index]
if not reverse_flg:
index += 1
# 配列の最後までインデックスが来ていたら反転
if index == length-1:
reverse_flg = True
else:
index -= 1
# 配列の最初、つまりゼロまできたら反転
if index == 0:
reverse_flg = False
yield ret
動作確認すると意図していた通りに出力ができています。
numbers = [1, 2, 3, 4]
for num in gen_round_trip(numbers, 9):
print(num)
>>>
1
2
3
4
3
2
1
2
3
別バージョンとして、dequeを用いた方法も考えてみました。
配列を2つ用意して一方から値を取り出して、入れ替えるようなイメージです。
def generate_round_trip_value(a_list, count):
if len(a_list) < 2:
raise ValueError("要素は2つ以上必要")
a_list = deque(a_list)
# 値の一時保持リスト
tmp = deque()
last = None
cnt = 0
while cnt < count:
# 値を取り出して一時保持リストに詰めていく
ret = a_list.popleft()
# [1,2,3,4] → [4,3,2,1]となっていく
tmp.appendleft(ret)
if not a_list:
a_list = tmp
tmp = deque()
# 配列の最初と最後が2回続くのを避ける
if ret == last:
continue
else:
last = ret
cnt += 1
yield ret
numbers = [1, 2, 3, 4]
for num in generate_round_trip_value(numbers, 9):
print(num)
>>>
1
2
3
4
3
2
1
2
3
この方法の分かりづらい点が変数のlastでしょうか。
配列をdequeのappendleftを使って反転させていっているのですが、何も手を加えないと値が2回続いて返されてしまいます。
a_listとtmpの動きを追うとこのようになります。
[1,2,3,4]
[]
# 1を取り出す
1 [2,3,4]
# 1をtmpにappendleft
[1]
2 [3,4]
[2,1]
3 [4]
[3,2,1]
4 []
[4,3,2,1]
# ここで取り出しが2回続いてしまう
4 [3,2,1]
[4]
これを避けるために変数lastに最後に返した値を保持しておいて、一緒だったらskipさせるという処理を加えています。
同じdequeを使うならrotateメソッドを使った方がシンプルでしょうか。
rotateメソッドは以下のような動きです。
numbers = [1, 2, 3, 4]
numbers = deque(numbers)
numbers.rotate(-1)
print(numbers)
>>>
deque([2, 3, 4, 1])
-1を指定すると左方向にベルトコンベアのように配列が組み変わります。
逆に1を指定すると右方向ですね。
numbers = [1, 2, 3, 4]
numbers = deque(numbers)
numbers.rotate(1)
print(numbers)
>>>
deque([4, 1, 2, 3])
この習性を利用して回転する方向を制御するとシンプルな実装になります。
def generate_round_trip_value(a_list, count):
a_list = deque(a_list)
n = -1
first = a_list[0]
last = a_list[-1]
for i in range(count):
ret = a_list[0]
if ret == first:
n = -1
if ret == last:
n = 1
a_list.rotate(n)
yield ret
numbers = [1, 2, 3, 4]
for num in generate_round_trip_value(numbers, 9):
print(num)
>>>
1
2
3
4
3
2
1
2
3
以上、今回はリストの値を往復させる方法でした。