制御構造 (Control Structures)


プログラムの制御構造

情報処理システムはその機能、性能、利便性の向上とともに年々大規模化しており、例えば現在のWindows やMac OSのプログラムの行数は、5000万行から1億行程度と推定されている。スマフォなどで遊んでいる身近なゲームも、単純なものでも数万行から数十万行のプログラムから構成されている。

このような大規模なソフトウェアを構成する膨大なプログラムの各行は、どのような順序で実行されているのだろうか。あるいは、自分がその一部でも作成する際には、どのように考えてプログラムを作成すればよいのだろうか。

プログラムの構造に関する研究の結果、どんなに複雑で大規模なプログラムでも、以下の3つの基本構造の組み合わせで作成できることが知られており、構造化定理と呼ばれている。また、この3つの基本構造でプログラムを構成することは、分かりやすく品質の高いプログラムを作成するために非常に重要だと考えられている。

  • 順次構造
  • 分岐(選択)構造
  • 反復構造

したがって、この3つの基本構造を理解し、コンピューターに実行させたい処理をこの3種類の基本構造に対応させてプログラムとして記述する方法を学べば、どんなに複雑で大規模なプログラムでも作成できることになる。

例えばFortran,Basic,Cなどの古い時期に設計されたプログラミング言語は、この3構造以外のプログラム構造を記述できる機能(goto文)が備わっており、往々にしてわかりにくいプログラムが作成される原因となっていたが、Python, JavaScript, Javaなどの設計の新しいプログラミング言語は、3つの基本構造以外の構造のプログラムが書けないようになっている。

少し難しいかもしれないが、プログラムの制御構造に関心がある者は、以下のページを出発点として探究するとよい。


順次構造

順次構造は、単純ではあるが、最も基本的で重要な制御構造である。

プログラムとして以下のようにn個の処理が記述されている場合に、上から順番に、取りこぼしなく、すべての処理が確実に実行される。

  • 処理1
  • 処理2
  •  :
  • 処理n

あまりにも当たり前のようだが、順次構造は、どんなに処理手順が多い複雑な処理でも、簡潔かつ確実にコンピュータに指示できることを表している。

順次構造のプログラム例を示す。プログラムの処理は上の行から順番に実行される。

radius = 5                 # 半径の定義
pi = 3.14159               # 円周率の定義
area = (radius * radius) * pi  # 面積の計算
print('半径', radius, 'の円の面積は', area)  # 結果の表示
半径 5 の円の面積は 78.53975

必要性

プログラムにおける順次構造の必要性を以下に示します。

  • 基本的な処理の流れ:順次構造は、プログラムの基本的な処理の流れを定義します。順次構造により、プログラムは通常、上から下へと順番に処理が実行されることが保証されます。
  • 予測可能な動作:順次構造により、プログラムの動作が予測可能になります。各処理が順番に実行されるため、プログラムの動作を理解しやすく、デバッグや保守が容易になります。
  • 他の制御構造との組み合わせ:順次構造は、分岐構造や反復構造と組み合わせて使用されます。これにより、複雑な処理を実現することができます。なお、条件分岐やループの中でも、基本的な処理は順次に実行されます。

意義

  • コードの可読性:順次構造は、コードの可読性を高めます。プログラムが順番に実行されるため、コードを読む人が処理の流れを追いやすくなります。
  • エラーの防止:順次構造を正しく理解していれば、処理の順番を間違えることによるエラーを防ぐことができます。例えば、変数の初期化を忘れたり、必要な処理を飛ばしたりすることがなくなります。
  • 効率的なプログラム設計:順次構造を利用することで、効率的なプログラム設計が可能になります。処理を順番に実行することで、無駄な処理を省き、プログラムの性能を向上させることができます。

注意点

  • 順番の重要性:順次構造では、命令の順番が非常に重要です。順番を間違えると、プログラムが期待通りに動作しない可能性があります。
  • 依存関係の管理:順次構造では、各命令が前の命令に依存していることが多いため、依存関係を正しく管理することが重要です。

分岐(選択)構造

コンピュータには、高速かつ正確に処理を行うだけでなく、状況判断を伴う処理を行える大きな特徴がある。その機能を実現するための制御構造がif文による分岐構造である。

必要性

  1. 柔軟な処理:分岐構造を使うことで、プログラムは異なる条件に応じて異なる処理を実行できます。これにより、ユーザーの入力や外部の状況に応じた柔軟な対応が可能になります。
  2. 効率的なコード:条件に基づいて処理を分岐させることで、無駄な処理を省き、効率的なコードを書くことができます。例えば、特定の条件が満たされた場合にのみ重い処理を実行することで、プログラムの性能を向上させることができます。
  3. 複雑な処理手順の実現:分岐構造を使うことで、複雑な処理手順を実現することができます。例えば、複数の条件を組み合わせて異なる処理を行う場合などです。

意義

  1. 条件に応じた動作:分岐構造は、プログラムが条件に応じて適切な動作をするための基盤となります。これにより、ユーザーの期待に応じた動作を実現できます。
  2. エラー処理:分岐構造を使うことで、エラー処理を適切に行うことができます。例えば、ユーザーの入力が不正な場合にエラーメッセージを表示するなど、プログラムの信頼性を向上させることができます。
  3. ユーザーエクスペリエンスの向上:条件に応じた適切なフィードバックや処理を行うことで、ユーザーエクスペリエンスを向上させることができます。例えば、フォームの入力内容に応じて適切なメッセージを表示するなどです。

if文 if - elif - else

if文は大きく4つの構造に分類できます。

  • 単純なif文
  • else節を伴うif文
  • elif節を伴うif文
  • elif節とelse節を伴うif文

基本的なif文

if 条件式:
    条件式が満たされている場合の処理

else節を伴うif文

if 条件式:
    条件式が満たされている場合の処理
else:
    条件式が満たされなかった場合の処理

else節を伴うif文を使用したプログラム例を以下に示す。

age = 20
if age >= 18:
    print('成人です')
else:
    print('未成年です')
成人です

elif節を伴うif文

if 条件式1:
    条件式1が満たされている場合の処理
elif 条件式2:
    条件式2が満たされている場合の処理
x = 7           # 7 で実行した後は3に変更して実行してみよう

if (x > 5):
    print('xは5より大きい')
else:
    print('xは5以下')

elif節とelse節を伴うif文

if 条件式1:
    条件式1が満たされている場合の処理
elif 条件式2:
    条件式2が満たされている場合の処理
else:
    条件式2が満たされなかった場合の処理

elif節は必要により複数記述することができる。

score = 85
if score >= 90:
    print("成績:秀")
elif score >= 80:
    print("成績:優")
elif score >= 70:
    print("成績:良")
elif score >= 60:
    print("成績:可")
else:
    print("成績:不可")
成績:優

elif節を使用する際の注意点

条件の順序: 条件は上から順に評価されるため、最初にTrueとなった条件のブロックが実行され、それ以降のelifelseブロックは無視されます。

score = 85
if score >= 60:
    print("成績:可")
elif score >= 70:
    print("成績:良")
elif score >= 80:
    print("成績:優")
elif score >= 90:
    print("成績:秀")
else:
    print("成績:不可")
成績:可

二次方程式の解

二次方程式 \( ax^2+bx+c=0\)の解は、以下の式で表されます。

\[x=\frac{-b \pm \sqrt{b^2-4ac}}{2a}\]

右辺の式の分子の平方根の中の式は、判別式と呼ばれDで示されます。

\[D=b^2-4ac\]

二次方程式は判別式Dの値により、実数解の数が異なります。具体的には判別式が正、0、負の場合それぞれで、解が2つ、重解、解なしとなります。

ここでは、判別式の値を分類し、解を計算、表示するプログラムを作るために、if文を使用したプログラムの処理手順を考えましょう。

処理手順

  • 方程式の係数と定数 a, b, c を設定
  • a, b,c から判別式の値を計算
  • 判別式が正ならば
    • 2つの解を計算し表示
  • 判別式が0ならば
    • 重解を計算し表示
  • それ以外(判別式が負)
  • 方程式の係数と定数 a, b, c を設定
  • a, b,c から判別式の値を計算
  • 判別式が正ならば
    • 2つの解を計算し表示
  • 判別式が0ならば
    • 重解を計算し表示
  • それ以外(判別式が負)

整理した処理手順に基づいて作成したプログラムを以下に示します。

import math

# 例: 2次方程式 2x^2 + 3x + 1 = 0 [(2x+1)(x+1)=0]の解を求める
a = 2
b = 3
c = 1

hanbetsu = b**2 - 4*a*c  # 判別式

if hanbetsu > 0:
    kai1 = (-b + math.sqrt(hanbetsu)) / (2*a)
    kai2 = (-b - math.sqrt(hanbetsu)) / (2*a)
    print("2つの解:", kai1, kai2)
elif hanbetsu == 0:
    kai = -b / (2*a)
    print("重解:", kai)
else:
    print("実数解なし")
2つの解: -0.5 -1.0

異なる二次方程式の解を求めるために、プログラムの最初の部分の変数設定値を以下に示すように変更して実行した例を示します。

# 例: 2次方程式 x^2 + 2x + 1 = 0 [(x+1)(x+1)=0]の解を求める
a = 1
b = 2
c = 1
重解: -1.0

もう一つの例も見てみましょう。

# 例: 2次方程式 x^2 + 2x + 3 = 0 の解を求める
a = 1
b = 2
c = 3
実数解なし

反復構造

プログラミングにおける反復構造(ループ)は、特定の処理を繰り返し実行するための重要な制御構造です。以下に、反復構造の必要性や用途について詳しく説明します。

必要性

  1. 効率化:同じ処理を何度も手動で書くのは非効率です。反復構造を使うことで、コードの重複を避け、効率的にプログラムを記述できます。
  2. 柔軟性:反復構造を使うことで、データの量や条件に応じて動的に処理を行うことができます。例えば、ユーザーからの入力に応じて処理を繰り返す場合などです。
  3. メンテナンス性:反復構造を使って繰り返し行う処理を一箇所にまとめることで、修正が必要な場合にその部分だけを変更すれば済むようにできます。これにより、コードの変更やメンテナンスが容易になります。

注意点

  • 無限ループの防止:反復構造を使用する際には、無限ループに注意が必要です。条件が常に真である場合、ループが終了せず、プログラムが停止しなくなります。
  • 条件の適切な設定:反復を終了する条件を適切に設定することが重要です。条件が適切でない場合、意図しない動作を引き起こす可能性があります。

反復構造は、プログラミングにおいて非常に強力なツールです。適切に使用することで、効率的で柔軟なプログラムを作成することができます。

while文とfor文の違いと使い分け

Pythonのfor文とwhile文は、どちらも繰り返し処理を行うための制御構造ですが、それぞれに適した用途や特徴があります。

  • while
    • while文は、特定の条件が満たされている間、繰り返し処理を行います。繰り返し回数が事前に決まっていない場合や、条件が変化するまで処理を続けたい場合に適しています。
  • for
    • for文は、シーケンス(リスト、文字列など)の各要素に対して繰り返し処理を行う場合に使用されます。繰り返し回数が明確に決まっている場合に適しています。

while文

while文は、条件式で指定する特定の条件が成立している間、繰り返し処理を行うための制御構文です。while文は、繰り返し処理を行う際に非常に便利です。特に、繰り返しの回数が事前に決まっていない場合や、特定の条件が満たされるまで処理を続けたい場合に使用されます。

条件の指定による繰り返し

  • 以下の処理を、指定された条件が満たされる間、繰り返し実行する
    • 処理

while文の基本的な構文は以下の通りです。

while 条件式:
    処理1
    処理2
      :
    処理n
n = 0
while n < 5:
    print(n)
    i = n + 1
0
1
2
3
4

numberが100未満である限り、numberを2倍にしその値を出力し続けるプログラムを示します。このプログラムは、numberが100以上になるとループが終了します。

number = 1

while number < 100:
    print(number)
    number *= 2
1
2
4
8
16
32
64

while文による汎用的な繰り返し処理の構造

制御変数の初期化
while 条件式:
    処理
    制御変数の更新

for文

回数の指定による繰り返し

range()が使用されている場合には、以下のように理解することができる。

  • 以下の処理を、指定された回数、繰り返し実行する
    • 処理
for 制御変数 in range(制御変数の初期値, 制御変数+1):
    処理
for n in range(0,5):
    print(n)
0
1
2
3
4

本来の処理は以下の通り

  • 与えられた集合のすべての要素に対して以下の処理を実行する
    • 処理

反復構造の制御

  • break
  • continue
  • pass


制御構造の演習

1から10までを足すといくつ?

1+2+3+4+5+6+7+8+9+10 -> ?

小学校の時に算数で計算法を習いましたよね。

真面目に一つずつ足し算をすると、ちょっと面倒なので、最初と最後、2番目と最後から2番目の数値を順に組み合わせると、1+10=11, 2+9=11...と5組の11にまとめられるので、解答は55という解法を学びました。

コンピュータプログラムでは、多数の数の足し算でも、ストレート、ある意味力づくですべての数を順に足すことができるので、そのプログラムを書いてみましょう。ストレートな加算をするプログラムは極めて簡単です。

while文での作成

まずは、while文を使ってプログラムを書いてみましょう。

処理手順

  • 合計値totalを0に初期化する
  • 加える値nを1に初期化する
  • nが10になるまで以下の処理を繰り返す
    • 合計値totalにnを加える
    • nを1増価させる
  • 合計値totalを表示する
total = 0              # 合計を初期化
n = 1                  # 加える値を初期化
while n <= 10:         # 10「以下」の値を加える
    total = total + n
    n = n + 1          # 加える値を更新:これを忘れると無限ループになります

print('1から10までの合計は', total)
1から10までの合計は 55

for文での作成

次に、同じ計算をforループで書いてみましょう。

処理手順

  • 合計値totalを0に初期化する
  • nが1から10の間以下の処理を繰り返す
    • 合計値totalにnを加える
  • 合計値totalを表示する
total = 0                # 総和を初期化
for n in range(1,11):   # 11(10+1)「未満」の値を加える
    total = total + n

print(total)
55

while文では、while文に入る前に制御変数nを明示的に0に初期化して、whileループ内の最後の処理で、次のループに備えてnを加算していました。

しかしながら、range()を使用して制御変数nをforループ内の処理を行うごとに、

total = 0                # 総和を初期化
n = 100                 # n をどのような値で初期化してもforループに影響ない
for n in range(1,11):   # 11(10+1)「未満」の値を加える
    total = total + n
    n = 100000          # n をどのような値で更新してもforループに影響ない

print(total)
55

プログラムの改良

上に示したプログラムは、1から10までの数の足し算しかできませんでした。

例えば、1から20までの足し算をしてもらいたいと思った場合には、3行目と7行目の10を20に修正する必要があります。誤ってどちらか一方だけを変更した場合には、計算内容と表示結果が一致しない状態、いわゆるバグのある状態になってしまいます。

このような変更を毎回行うのは大変なので、少し汎用性のあるプログラムに修正しましょう。

たし算の上限値を変数に格納するようにし、上限値を必要とする場所すべてでその変数を使用するように変更しました。これで、一か所だけの上限値の設定で誤りなく計算できるようになりました。

jyougen = 20           # 1から加算する数の上限

total = 0              # 合計を初期化
n = 1                  # 加える値を初期化
while n <= jyougen :   # jyougen 「以下」の値を加える
    total = total + n
    n = n + 1          # 加える値を更新:これを忘れると無限ループになります

print('1から', jyougen, 'までの合計は', total)
1から 20 までの合計は 210

プログラムを改良し、上限値の変更を簡単にでき、バグも生じないプログラムにできましたが、足し算の上限値を変更するためには、プログラムの1行目を毎回変更しなければなりません。

加算する上限値をプログラムの開始時に指定できるようにしましょう。

このような変更で、上限値は自由に変更でき、さらに、プログラムに手を加えずに様々な上限値を指定して計算できるようになりました。

jyougen = int(input('足し算の上限値:')) # 1から加算する数の上限

total = 0              # 合計を初期化
n = 1                  # 加える値を初期化
while n <= jyougen :   # jyougen 「以下」の値を加える
    total = total + n
    n = n + 1          # 加える値を更新:これを忘れると無限ループになります

print('1から',jyougen,'までの合計は', total)

演習

改良版のfor文での書き換え

小学校方式の計算


基本演習


素数の判定

基本プログラム

正の整数。

1は素数ではない。

判定したい数よりも小さな整数で割り切れれば素数ではない。

import math

n = 101       # 素数か否かをテストする数値

is_prime = True
if n < 2:     # 2未満の数は素数ではない
    is_prime = False
else:
    for i in range(2, n): # 2からn-1までの数で割り切れるか確認
        if n % i == 0:
            is_prime = False
            break

if (is_prime == True):
    print(n, 'は素数です')
else:
    print(n, 'は素数ではありません')
101 は素数です

二重のforループを使用した九九の表の作成

for i in range(1,10):
    for j in range(1,10):
        print(f'{i*j:3d}', end='')
    print()
  1  2  3  4  5  6  7  8  9
  2  4  6  8 10 12 14 16 18
  3  6  9 12 15 18 21 24 27
  4  8 12 16 20 24 28 32 36
  5 10 15 20 25 30 35 40 45
  6 12 18 24 30 36 42 48 54
  7 14 21 28 35 42 49 56 63
  8 16 24 32 40 48 56 64 72
  9 18 27 36 45 54 63 72 81

英語と数学がともに80点以上の生徒のピックアップ



発展課題


探究課題