制御構文を学ぶ

前回までで、チュートリアルを見ながら簡単なPythonのコードを書けるようになったはずだ。インデント、文字列、リストといったPythonでも基本中の基本となる部分は理解できるようになっていると思う。今回もThe Python Tutorialを読み進めながらPythonについて理解を深めていこう。

前回は最後にwhile制御構文を使ったフィボナッチ数列を生成するコードについて勉強した。今回もチュートリアルの流れに習って制御構文の基本となる「if」と「for」について取り上げる。

if制御構文

チュートリアルに掲載されている最初のif制御構文のサンプルは、Pythonのif制御構文について多くのことを教えてくれる(以下参照)。

>>> x = int(input("Please enter an integer: "))
Please enter an integer: 1234
>>> if x < 0:
...  x = 0
...  print('Negative changed to zero')
... elif x == 0:
...  print('Zero')
... elif x == 1:
...  print('Single')
... else:
...  print('More')
...
More
>>>

まず、if制御構文ではif、「elif」「else」というキーワードが使われている。「if」「elif」「else」に続く処理はインデントしてグループ化する必要があることもわかる。また、「if」「elif」「else」の行の最後に「:」が指定されていることにも気がつくだろう。これがいわゆる”Python的”な書き方だ。人気の高いほかの汎用プログラミング言語とはちょっと違う書式だが、慣れれば問題ないだろう。

for-in制御構文

for-in制御構文は、ほかのプログラミング言語では「foreach」など、別の名前が使われていることもある。「in」の前に値が代入されることになる変数名を、「in」の後に大元のデータが格納されたものを書くスタイルが多いように思う。

Pythonのチュートリアルに掲載されているサンプルでは、次のように「in」の後にリストを指定し、そのなかから値が順番に変数wに代入された状態でグループの処理が実行される仕組みになっている。

>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...  print(w, len(w))
...
('cat', 3)
('window', 6)
('defenestrate', 12)
>>>

if制御構文のときと同じように、「for」の行の最後に「:」が記述されているのは最近のほかの人気の高いプログラミング言語には見られないPythonに独特の書き方だ。

下記のサンプルはfor-inの大元のデータとしてリストのスライス機能を使って生成したリストを割り当て、スライスの元となったリストにはループの中で値を追加している。

>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...  if len(w) > 6:
...   words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
>>>

このサンプルは、リストのスライスの特性をよく表している。このサンプルコードを見て「なるほどね」と納得するようなら、かなりPythonのコードを理解できている。

range()を使う

シェルスクリプトであれば、「seq」というコマンドを使って数字の連続データを生成し、その値をfor-in制御構文で使うことがある。seqコマンドは、例えば次のような動作をする。

$ seq 1 10
1
2
3
4
5
6
7
8
9
10
$

Pythonにも同じような機能が用意されている。range()と呼ばれる関数で、この関数を使うことで連続した数字データをリストとして生成することができる。

>>> for i in range(5):
...  print(i)
...
0
1
2
3
4
>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]
>>>

range()は上記サンプルのように、正の値を1つだけ指定すれば、0から1つずつ加算された自然数を指定された個数分だけ含むリストを生成してくれる。2つの値を指定すれば1つ目の値から2つ目の値の1つ前まで1ずつ加算した数字のリストを、3つの値を指定すれば3つ目に指定された値を加算分として作成したリストを作成してくれる。

リストとrange()を組み合わせると、次のような使い方もできる。

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...  print(i, a[i])
...
(0, 'Mary')
(1, 'had')
(2, 'a')
(3, 'little')
(4, 'lamb')
>>>

このように、Pythonではデータを格納する方法としていろんな場所でリストが登場する。リストはまずマスターしておく必要があるデータ格納方法だということがおわかりいただけたのではないだろうか。

break

for-in制御構文のような繰り返し制御構文を扱う場合には、フローを制御する機能である「break」と「continue」についても理解しておく必要がある。次のサンプルはfor-in制御構文とif制御構文を組み合わせたもので、さらに内部で「break」が使われている。for-in制御構文のネスト構造と、「break」によるループ抜けの深さを理解するには良いサンプルだ。

>>> for n in range(2, 10):
...  for x in range(2, n):
...   if n % x == 0:
...    print(n, 'equals', x, '*', n//x)
...    break
...   else:
...    # loop fell through without finding a factor
...    print(n, 'is a prime number')
...
(3, 'is a prime number')
(4, 'equals', 2, '*', 2)
(5, 'is a prime number')
(5, 'is a prime number')
(5, 'is a prime number')
(6, 'equals', 2, '*', 3)
(7, 'is a prime number')
(7, 'is a prime number')
(7, 'is a prime number')
(7, 'is a prime number')
(7, 'is a prime number')
(8, 'equals', 2, '*', 4)
(9, 'is a prime number')
(9, 'equals', 3, '*', 3)
>>>

出力結果を追っていくと、「break」がどのように機能しているかがよくわかる。「break」は記載された条件を満たすと到達するわけだが、「break」に到達すると「break」から最も近いfor-in制御構文を抜けていることがわかる。しかし、抜けるそのはそこまでだ。一番大外のfor-in制御構文を抜けることはなく、最後まで処理が行われている。

これはほかの多くのプログラミング言語にある「break」の処理とよく似ている。「break」は、直近の繰り返し構文を抜ける指定として機能していることがわかる。

continue

次は「continue」だ。「continue」は「break」とは異なりループを抜けることはなく、「直近のループの次の条件判断まで飛ぶ」という処理になる。次のサンプルを見るとよくわかる。

>>> for num in range(2, 10):
...  if num % 2 == 0:
...   print("Found an even number", num)
...   continue
...  print("Found a number", num)
...
('Found an even number', 2)
('Found a number', 3)
('Found an even number', 4)
('Found a number', 5)
('Found an even number', 6)
('Found a number', 7)
('Found an even number', 8)
('Found a number', 9)
>>>

このサンプルは「continue」の特性をよく表している。「break」と「continue」は結構使う機能なので、どこにジャンプするのかはよく理解しておこう。

pass

Pythonには「break」と「continue」と雰囲気がよく似たキーワードに「pass」がある。これは開発中に便宜的に用いられることがある機能で、知っておくと便利かもしれない。「pass」の機能は「何もしない」だ。例えば、次のようにwhile制御構文のなかに「pass」だけを書いておくと、「pass」は何もしないのでwhile制御構文は無限ループになる。

>>> while True:
...  pass  # Busy-wait for keyboard interrupt (Ctrl+C)
...             ← ここでCtrl-Cを押す
KeyboardInterrupt
>>>

この機能は次のように空のクラスを定義する場合や、空の関数を定義する場合に使われることがある。とりあえず定義しておいて、中身は後で書くといった場合、このように「pass」を書いておくわけである。

>>> class MyEmptyClass:
...  pass
...
>>> def initlog(*args):
...  pass   # Remember to implement this!
...
>>>

この機能があるととりあえず動くものまでコーディングを進めるといったことができて便利だ。

Pythonはかなり理解しやすいプログラミング言語だ。学習コストの低さと応用に利用できるサードパーティ製ライブラリの豊富さの魅力を、ここまでのチュートリアル読みを通じて体感できたのではないかと思う。これだけ簡単なサンプルだが、C言語で同じことをしようとするとかなり面倒だ。Pythonは覚えやすく、最初の学習を阻害するような複雑な思考も要求されないので、チュートリアルベースで理解しながら進めることができると思う。

【参考資料】
The Python Tutorial