コンピュータ演習II 第09回 整数以外のリスト、range の応用、入れ替え¶

第05回でリスト、第06回で繰り返し(for 文)を学びました。今回は、その2つを組み合わせて、次の3つを学びます。

  1. 整数以外のリスト(文字列を要素にしたリスト)
  2. range の応用(とびとびに進む「ステップ」、後ろから前へ進む「逆順」)
  3. 要素の入れ替え(swap)(リストの要素を交換し、並び替える)

1. 整数以外のリスト¶

1.1 文字列のリスト¶

リストの要素は、整数だけでなく 文字列 でもかまいません。次の fruits は、5 つの文字列を要素に持つリストです。

アクセスの仕方は、整数のリストとまったく同じです。fruits[0] で先頭の要素("apple")が取り出せますし、len(fruits) で要素の個数(5)が分かります。

文字列のリスト fruits

In [ ]:
fruits = ["apple", "banana", "cherry", "melon", "lemon"]   # 要素が文字列のリスト

print(fruits[0])        # 先頭の要素 -> apple
print(fruits[2])        # インデックス 2(3 番目)の要素 -> cherry
print(len(fruits))      # 要素の個数 -> 5

1.2 文字列リストを for 文で処理する¶

整数のリストと同じように、for 文と range(0, len(fruits)) で全要素を順に処理できます。要素が文字列でも、取り出し方(fruits[i])や繰り返し方は、整数のリストとまったく同じです。

In [ ]:
fruits = ["apple", "banana", "cherry", "melon", "lemon"]

# i は 0, 1, 2, 3, 4 と変わり、全要素を順に取り出す
for i in range(0, len(fruits)):
    print(fruits[i])            # i 番目の要素を出力

1.3 文字列も「文字の並び」として扱える¶

実は、文字列もリストと同じように「文字が並んだもの」 として扱えます。word[0] で先頭の文字、word[4] で5 番目の文字が取り出せます。インデックスが 0 から始まるところも、リストとまったく同じです。

また、len() は、リストの要素数だけでなく、文字列の文字数 を数えるのにも使えます(len("apple") は 5)。

for 文と組み合わせれば、文字列の 1 文字ずつ を順に処理できます。

文字列 "hello" のインデックス

In [ ]:
word = "hello"

print(word[0])        # 先頭の文字 -> h
print(word[4])        # インデックス 4 の文字 -> o
print(len(word))      # 文字数 -> 5

# for で 1 文字ずつ取り出す(リストとまったく同じ書き方)
for i in range(0, len(word)):
    print(i, word[i])           # インデックスi と i 番目の文字を出力

1.4 後ろから数えるインデックス(負のインデックス)¶

インデックスに 負の数 を使うと、後ろから 数えられます。numbers[-1] はいちばん後ろ(末尾)の要素、numbers[-2] はその1 つ前です。

これまで末尾の要素は numbers[len(numbers) - 1] と書いてきましたが、numbers[-1] ならもっと短く書けます。文字列でも同じように使えます(word[-1] で最後の文字)。

前から数えるインデックスと後ろから数えるインデックス

In [ ]:
numbers = [10, 20, 30, 40, 50]

print(numbers[-1])    # いちばん後ろ(末尾) -> 50
print(numbers[-2])    # 後ろから 2 番目 -> 40

# これまでの len を使った書き方と同じ結果になる
print(numbers[len(numbers) - 1])   # -> 50 (numbers[-1] と同じ)

word = "hello"
print(word[-1])       # 最後の文字 -> o

2. range の応用(3 つ目の引数)¶

これまでは range(開始, 終了) の形で、1 ずつ増える繰り返しを作ってきました。実は range には 3 つ目の引数 があり、range(開始, 終了, ステップ) と書くと、増える量(ステップ)を変えられます。

2.1 とびとびに進む(ステップ)¶

ステップに 2 を指定すると、2 ずつ増えます。range(0, 11, 2) は 0, 2, 4, 6, 8, 10 です。終わりの値 11 は 含まない(手前で止まる)のは、これまでと同じです。

range(0, 11, 2) の数直線

In [ ]:
# range の 3 つ目の数(ここでは 2)が「いくつずつ増えるか」(ステップ)を表す
for even in range(0, 11, 2):   # 0 から 2 ずつ、11 の手前まで
    print(even)                # 0, 2, 4, 6, 8, 10 が順に出る

リストと組み合わせると、偶数番目(インデックス 0, 2, 4, ...)の要素だけ を処理する、といったことができます。

In [ ]:
numbers = [10, 20, 30, 40, 50]

# インデックスを 2 ずつ進めると、偶数番目(0, 2, 4)の要素だけを取り出せる
for even in range(0, len(numbers), 2):   # even = 0, 2, 4
    print(numbers[even])                 # 偶数番目の要素 -> 10, 30, 50

2.2 逆順(後ろから前へ)¶

ステップに 負の数 を指定すると、数は 減って いきます。後ろから前へ進みたいときに使います。range(10, -1, -1) は 10, 9, 8, ..., 1, 0 です。

終わりの値は含まないので、0 まで出したいときは、その1 つ先の -1 を終わりに指定します。

range(10, -1, -1) の数直線

In [ ]:
# ステップを -1 にすると、数は 1 ずつ減っていく(後ろから前へ進む)
for reverse in range(10, -1, -1):   # 10 から始めて、-1 の手前(=0)まで
    print(reverse)                  # 10, 9, 8, ..., 1, 0 が順に出る

リストを 後ろから順に 処理するときは、range(len(numbers) - 1, -1, -1) とします。先頭のインデックスは len(numbers) - 1、最後は 0(その手前の -1 を終わりに指定)です。

In [ ]:
numbers = [10, 20, 30, 40, 50]

# 最後のインデックス len(numbers)-1 から 0 まで、後ろ向きにたどる
for reverse in range(len(numbers) - 1, -1, -1):   # reverse = 4, 3, 2, 1, 0
    print(numbers[reverse])                       # 後ろから順に -> 50, 40, 30, 20, 10

3. 入れ替え(swap)¶

3.1 2 つの値の入れ替え(なぜ変数 tmp が必要なのか)¶

2 つの変数 x と y の中身を入れ替えたいとします。しかし、いきなり x = y としてしまうと、x のもとの値が 消えて しまい、あとから取り戻せません。

そこで、片方の値を一時的に別の変数(ここでは tmp と呼びます)へ 逃がして から入れ替えます。 次の3 ステップで行います。

tmp を使った入れ替え

In [ ]:
x = 6
y = 1
tmp = 0     # 一時変数、temporary の略でtmp と名付けることが多い

# いきなり x = y とすると、x のもとの値(6)が消えて取り戻せない。
# そこで tmp に片方を逃がしてから、3 ステップで入れ替える。
tmp = x      # tmp に x のもとの値(6)を保存しておく
x = y        # x に y の値(1)を入れる
y = tmp      # y に、保存しておいた 6 を入れる

print(x, y)  # 1 6

tmp は temporary(一時的)の略で、値を一時的に置いておくための変数です。ここでは入れ替えのための入れ物として使うので、tmp という名前で十分です。

3.2 リストの隣り合う2 要素の入れ替え¶

同じやり方で、リストの 隣り合う2 つの要素 numbers[i] と numbers[i+1] も入れ替えられます。考え方は変数のときとまったく同じで、tmp に片方を逃がしてから入れ替えます。

In [ ]:
numbers = [6, 1, 2, 5, 0]
i = 0        # 入れ替えたい位置(ここでは 0 番目と 1 番目)

# 変数 x, y のときと同じ 3 ステップ。tmp に片方を逃がしてから入れ替える。
tmp = numbers[i]              # numbers[0](=6)を tmp に逃がす
numbers[i] = numbers[i + 1]   # numbers[0] に numbers[1](=1)を入れる
numbers[i + 1] = tmp          # numbers[1] に、逃がしておいた 6 を入れる

print(numbers)   # [1, 6, 2, 5, 0]

3.3 条件つきの入れ替え(if と組み合わせる)¶

「隣り合う2 つを比べて、前のほうが大きいときだけ 入れ替える」という処理を考えます。if で大小を判定し、条件が成り立つときだけ入れ替えます。こうすると、隣り合う2 つは必ず 小さい方が前、大きい方が後ろ に並びます。

In [ ]:
numbers = [6, 1, 2, 5, 0]
i = 0

# 前(numbers[i])のほうが大きいときだけ入れ替える。
# こうすると、この隣り合う 2 つは必ず「小さい方が前、大きい方が後ろ」になる。
if numbers[i] > numbers[i + 1]:
    tmp = numbers[i]
    numbers[i] = numbers[i + 1]
    numbers[i + 1] = tmp

print(numbers)   # [1, 6, 2, 5, 0]

4. 繰り返しを用いた要素の移動¶

3.3 の「条件つき入れ替え」を、先頭のペアから末尾のペアまで順に くり返します。隣り合う numbers[i] と numbers[i+1] を比べ、前のほうが大きければ入れ替える、という処理を先頭から末尾まで行います。

for のインデックスは range(0, len(numbers) - 1) とします。最後のペアは numbers[len(numbers)-2] と numbers[len(numbers)-1] なので、i は len(numbers) - 2 まで動けば十分だからです(range(0, len(numbers)-1) は 0 から len(numbers)-2 まで)。

こうして先頭から末尾まで隣同士を比べていくと、いちばん大きい要素が、いちばん後ろまで運ばれていきます。

隣同士を比べていくと最大値が後ろへ

In [ ]:
numbers = [6, 1, 2, 5, 0]

# 左から順に、隣り合うペア(numbers[i] と numbers[i+1])を比べていく。
# i は 0 から len(numbers)-2 まで(最後のペアは numbers[len-2] と numbers[len-1])。
for i in range(0, len(numbers) - 1):  # i = 0, 1, 2, 3
    if numbers[i] > numbers[i + 1]:   # 前のほうが大きければ
        tmp = numbers[i]              # tmp を使って入れ替える
        numbers[i] = numbers[i + 1]
        numbers[i + 1] = tmp

print(numbers)   # [1, 2, 5, 0, 6]  <- いちばん大きい 6 が後ろへ

この「先頭から末尾までの比較・入れ替え」を 並び終わるまで何度もくり返す と、リスト全体がきれいに並びます。これが 並べ替え(整列) で、第11回でくわしく扱います。今回は、まず この処理を確実に書けるようにしておきましょう。

5. フローチャートで見る¶

今回の課題は、フローチャートを組んで提出します(ツールの使い方は、課題の説明と別紙のクイックリファレンスを参照してください)。ここでは、今回の中心である入れ替え(swap)と、4 章の「繰り返しで隣どうしを比べる処理」が、フローチャートでどう見えるかを確認しておきます。

5.1 配列要素の入れ替え(tmp 経由)¶

入れ替えは、Python と同じ3 ステップ(tmp = numbers[i] → numbers[i] = numbers[i+1] → numbers[i+1] = tmp)を、変数・値のブロックで順に並べるだけです。条件つきにするときは、これらを条件分岐(if)の True 側に入れます。

フローチャートでの入れ替え

5.2 繰り返しで隣どうしを比べる流れ¶

4 章の「繰り返しを用いた要素の移動」をフローチャートにすると、下のようになります。繰り返しは for のひし形で作り、中身は Python とまったく同じ for i in range(0, len(numbers) - 1): を書きます。「次あり」の側に「if(numbers[i] > numbers[i+1])→ 入れ替え」を入れると、先頭から末尾まで隣どうしを比べて交換する流れになります。

隣どうしを比べて交換する流れ

まとめ¶

  • リストの要素は 文字列 でもよい。アクセスや len() の使い方は整数のリストと同じ。文字列そのものも「文字の並び」 として、word[0] や len(word) で1 文字ずつ扱える。
  • インデックスに 負の数 を使うと 後ろから 数えられる。numbers[-1] は末尾、numbers[-2] はその1 つ前(numbers[len(numbers)-1] の短い書き方)。
  • range(開始, 終了, ステップ) の3 つ目の引数で、とびとび(range(0, 11, 2))や 逆順(range(10, -1, -1))の繰り返しが作れる。終わりの値は含まない。
  • 2 つの値の 入れ替え(swap) は、tmp に片方を逃がしてから行う。if と組み合わせると条件つきの入れ替えになる。
  • 隣り合うペアを 先頭から末尾まで順に 条件つきで入れ替えると、最大の要素が後ろへ運ばれる。これを何度もくり返すと並べ替え(第11回)になる。