step04 リストとタプル

リストとスカラー

リストは、他の言語でいう配列と同じデータ構造※1で、1つの名前で複数の値を格納できる変数である。これに対し、これまで使ってきた普通の変数、つまり1つだけの値を格納できる変数を、スカラーと呼ぶ。
スカラーとリストの違いは、一軒家と集合住宅とか、1輌の車と列車をイメージするとよく分かる。後者のイメージで描いたのが、図4-1である。

図4-1: スカラー変数とリスト変数
簡単のために、ここでは1輌に1人の乗客(つまり1つの値)しか乗せられないものとする。スカラー変数nには、10という値が入っている。一方、リスト変数aは、10輌編成になっており、10個の値が格納されている。
ただし、格納しただけでは、ある値が何番目に乗っているかわからないので、それを明示するための号車番号に当たるのが、インデックスという整数である。現実の列車と異なるのは、インデックスが0から始まることで、10個のリストであれば、インデックスは0~9である。インデックスは、リスト変数名の後に[ ]をつけ、そこに入れる決まりである。つまり、図4-1の状態は、

# スカラー変数
n = 10

# リスト変数
a[0] = 51
a[1] = 86
a[2] = 37
  :
a[9] = 23
と書くことができる。リスト変数への代入は、まとめて、

a = [51, 86, 37, … , 23]
と書いてもよい。

1 もちろん、細かい相違はある。たとえば、Perl言語のような、配列変数のスカラーコンテキストリストコンテキストの区別はない。pythonはプログラムを読みやすくするために、慣れれば便利な構文のいくつかをあえて排除している節がある。

プログラム中で、リストを作るのは簡単だ。要素を並べて[ ]で括るだけでよい。例を示す。

fruits = ['キウイ', 'パパイヤ', 'マンゴー']
vegitables = ['玉ねぎ', 'ジャガイモ', 'トマト']
foods = [fruits, vegitables]      # リストのリスト
empty_list = []                        # 空のリスト
他の多くの言語のような、多次元のリストはないが、リストの要素としてリストを格納できるし、その内側要素に多次元リスト風にアクセスできるから、不自由はしない。たとえば、上の例では、 である。

タプルとは

タプルは、リストとほぼ同じだが、中の要素は定義時に固定され、値を後から追加、削除、変更できないタプルでできることはすべてリストでもできるので、初心者のうちはリストだけを使った方がよいかもしれない。処理速度データ容量などが問題になったときに、値が変更されないと分かっているデータを、リストからタプルに変更すれば対応できるからだ。
タプルを作るには、要素を並べるだけでよいが、分かりやすくするために( )で括ることが認められている(つまり、リストが[ ]を使うのに対しタプルでは( )を使う、というわけではないのだ。ここは初心者が勘違いしやすい点である)。例を示す。


fruits_taple = 'キウイ', 'パパイヤ', 'マンゴー'
vegitables_taple = ('玉ねぎ', 'ジャガイモ', 'トマト')
meat_taple = 'マトン',          # 値が1つだけの時には、最後に「,」が要る
empty_taple = ()                # 空のタプル
タプル回りの文法が微妙に混乱しているように感じるのは、私だけだろうか。
タプルのタプルや、タプルのリストも作れるが、初心者は混乱しそうだから、手を出さない方が無難だ。以下のサンプルプログラムでは、リストとだけを用いている。

平均身長を求める

冒頭で代入された10人分の身長データ「tall」を元に、平均身長を計算する。


リスト4-1: average.py

このfor文では、リストの要素それぞれが変数each_tallに代入されてループし、最後の要素を処理し終われば脱出する。リスト(やタプル)とfor構文がうまくマッチした、わかりやすい表現である。
【      空欄      】部分では、リストの長さ(要素数)を取得する関数が必要である。
最終行の


print('average:{:.02f}'.format(ave))
は、やや分かりにくいが、変数aveを「小数点下2桁の浮動小数点数として出力する」処理である。実は、python言語において、文字列はオブジェクトであり、文字列クラスで定義されているformat()メソッドを呼び出しているのである。クラスとオブジェクトについては【応用編】に任せるが、format()メソッドによる書式指定は、step08で解説している。

100までの素数をリストアップ(改良版)

ようやく、step03で作った「素数」のプログラムに改良を施すチャンスがやってきた! あの時、効率がたいへん悪いと知りながら何もできなかったのは、まだスカラー変数しか使えなかったからだ。多くの素数について「素数かどうか」「調べるべきか」をチェックし、記憶しておく手段がなければ、2から100までシラミ潰しに調べる以外に方法はないからである。しかしリストを使えば、素数の倍数を非素数にマークし、後のチェックから除外できるので、大幅に効率はよくなるはずだ。
このアルゴリズムはエラトステネスのふるい(Eratosthenes' Sieve)といい、紀元前3世紀のギリシャの哲学者・エラトステネスが発明したものとされる。この原理を示すすばらしいGIFアニメーションを見つけたので、図4-2で紹介する。しばらく眺めているだけで、アルゴリズムがたちまち理解できる※2

2 このような動く図の説明力は、教材作成などにもっと活かされてもよいと思う。説明のための最強のメディアといっても決して過言ではない。

図4-2: エラトステネスのふるい(参考:Wikipedia)

2から120までの整数を記した碁盤目のようなものがふるいである。すべての整数はまず、無条件に「素数」としてマークされている(定義上、1は素数ではないので除外されている)。つぎに、素数である2がリストアップされるが、その際、すべての2の倍数は「非素数」とマークされ、以後のテストから除外される。同様の手順を3、5、7……について繰り返せば、多くの整数が「非素数」として「ふるい落とされる」ので、最後に残った整数が素数である。
いままで、このアルゴリズムを実現できなかったのは、この「ふるい」に相当するデータ構造が作れなかったからだ。そこで、リストprimeを使って「ふるい」を作り、同じアルゴリズムを実現したのがリスト4-2である。


リスト4-1: prime100m.py


from math import sqrt
は、math(数学関数)モジュールから平方根関数sqrt()だけを参照したい場合の書き方である。この場合、プログラム中ではmath.sqrt()ではなくsqrt()だけで呼び出す。このようにpythonでは、モジュールから関数という「部品」をバラでインポートできる。
最初に100要素からなるリストprimeを、すべて定数Trueで初期化している。ここではリストの代入が起こっている。リストと整数の乗算がどのような意味で定義されているかを理解しよう。
prime[1]~prime[99]までしか使っていないことに注意。たとえばprime[96] == Trueは、整数97が素数であることを表す。11以上はテストせず、ふるい残された数をそのままリストアップしていることにも注意してほしい。

性能向上の確認

リスト4-1のプログラムで性能が向上したことを確かめるために、調べる数の上限を100000や1000000にして、実行時間を測定し、step03演習の結果と比較すること。