2023-2I プログラミング1 第06回 講義資料

2023年06月1日(木)5・6時限

1 準備

2 インデントとブロック

Pythonでは、インデント (字下げ) により ブロックの範囲 を指示します。

ブロックとは C/C++言語 (Arduino言語を含む) や JavaScript などのプログラミング言語では {} の記号によって与えるもので、例えば、繰返し構文や条件分岐構文の適用範囲 (どこからどこまでの範囲を繰り返すのか、条件を満たすときにどこからどこまでの範囲を実行するのか) を指示するときになどに利用されます。

Pythonにおいて、ブロックは次のように 同じ幅を持ったインデント によって指示します。一般にインデントは 半角スペース4個 を使って表現しますが、この講義資料では可読性の観点から 半角スペース2個 を使用します。

次のプログラムを Google Colab. のノートブックに貼り付けて実行し、ブロックの内部では インデント幅を同じにしなければならない ことを確認してください。

for i in range(3):
  print('AA') # 幅2

for i in range(2):
    print('BBB') # 幅4
    print('CCC') # 幅4

for i in range(2):
  print('DDDD')
    print('EEEE')
 print('FFFF')
DDDD
EEEE
FFFF
DDDD
EEEE
FFFF

なお、インデントには「タブ文字」を使うこともできます。ただし、画面上の見かけの文字幅が同じであっても、「タブ文字」と「スペース」は別物なので、ブロック内で両者を混在させることはできません。

また、インデントは「全角スペース」では機能しません。実際に全角スペースを使ってインデントを与え、どのようなエラーが生じるかを確認してください

2.1 演習①

次の各プログラムを実行したとき、どのような出力 (実行時エラーを含む) が得られるかを予想せよ(=脳内でプログラム動作をシミュレートせよ)。また、実際に Colab. ノートブックで実行して、予想と一致するか確認せよ。

for i in range(3):
  print('AA')
  print('BBB')
print('CCCC')
for i in range(3):
    print('AA')
    print('BBB')
print('CCCC')
for i in range(3):
print('AA')
print('BBB')
print('CCCC')
for i in range(3):
    print('AA')
  print('BBB')
print('CCCC')
for i in range(3):
  print('AA')
    print('BBB')
  print('CCCC')

3 課題取組みのフィードバック

3.1 ColabTurtleのカメの移動速度

複雑な図形を描く場合は、カメ(カーソル)の移動速度の初期値 (initial_speed) を最大値である 13 にすると短時間で処理が終了します。

import ColabTurtle.Turtle as t
t.initializeTurtle(initial_speed=13,initial_window_size=(400,400))

3.2 繰返し回数を整数値で与えるためのTips

for i in range(10): のような構文で繰返し処理をする場合、range に与える引数を 整数 にしないと実行時エラーが発生します。実際に確認してみましょう (以下では range の引数 (=繰り返し回数) を整数ではなく 実数 で与えています) 。

%reset -f
n=7
for i in range(360/n): # 360/7=51.42.....
  print('AA')

実行すると Type Error(型の誤り) が発生します。

図形処理では range の引数を数式で与えたい場合がありますが (例えば360/n のようなケース)、このような場合、実行時エラーを回避するためには次のような工夫をします。

%reset -f
n=7
for i in range(360//n): # / の代わりに // を使用
  print('AA')
%reset -f
n=7
for i in range(int(360/n)): # int() を使用
  print('AA')

また、目的に応じて range(360//n+1) のように回数を調整します。

3.2.1 演習②

次のプログラムの実行結果 (実行時エラーを含む) を予測し、実際に実行して結果を確かめよ (int() では 四捨五入されるのか、それとも切り捨てや切り上げが起こるのか? という疑問を持てることが大事)。

%reset -f
print(int('79'))   # 引数に整数の文字列
print(int('79'))  # 引数に全角整数の文字列
print(int('16.9')) # 引数に実数値の文字列
print(int(16.9))   # 引数に実数値
print(int('-16.9')) # 引数に実数値
print(int(-16.9))   # 引数に実数値
print(int(15.6+7.9))   # 引数に実数値の式

3.3 Colab.のテキストセルにおける数式記述

Colab.のテキストセルでは マークダウン記法文字装飾ができることを既に紹介していますが、さらに、TeX記法による数式の記述 も可能です(TeXテフ と読みます)。

例えば、Colab. のテキストセルのなかで $$$$ で囲んで次のように記述すると…

$$ \sum_{k=1}^{10}(3k^{2}-1) $$

$$ \sum_{k=1}^{n}k^{2}=\frac{1}{6}n(n+1)(2n+1) $$

$$ \sum_{k=1}^{n}(a_{n}\pm b_{n})=\sum_{k=1}^{n}(a_{n})\pm \sum_{k=1}^{n}(b_{n}) $$

以下のように 整形された数式が画面表示 されます。

\[ \sum_{k=1}^{10}(3k^{2}-1) \]

\[ \sum_{k=1}^{n}k^{2}=\frac{1}{6}n(n+1)(2n+1) \]

\[ \sum_{k=1}^{n}(a_{n}\pm b_{n})=\sum_{k=1}^{n}(a_{n})\pm \sum_{k=1}^{n}(b_{n}) \]

実際にコピペして確認してみましょう。記法の詳細は「TeX 数式」などでキーワード検索してみてください。

なお、独立式ではなく文章中に数式を入れたい場合は、次のように $$ で囲みます。

ここで $a_{n}$ はXXXであり・・・

4 条件分岐構文

条件分岐構文 (通称「if文」) は、処理の流れを変えたい (つまり分岐させたい) ときに使用します。具体的には条件式を与えて、それが「成立するか (であるか)」「成立しないか (であるか)」によって実行するコードを変えます。

繰返し構文と同様に 処理の範囲 (=ブロック) をインデントにより指示 します。

実例で考えてみましょう。次のプログラムは、サイコロを投げて「1~4の目がでたとき」は「はずれ」、一方で「5または6がでたとき」は「あたり」の処理をするものです。実際に実行して動作を確認してください。

%reset -f
import random

for i in range(5): # 5回チャレンジ

  dice_num = random.randint(1,6) # [1,6]の範囲の乱数生成
  print(f'サイコロで{dice_num}がでたので「',end='')

  # 条件分岐構文
  if dice_num <= 4 :  # 末尾のコロン (:) を忘れがちなので注意
    print('はずれ',end='')
  else :             # 末尾のコロン (:) を忘れがちなので注意
    print('あたり',end='')

  print('」です。')

なお、Python言語以外、例えば C/C++言語 (Arduino言語を含む) などでは、条件式を () で囲みますが、Python言語では囲まない ので注意してください。

4.1 アタマを柔らかくして分岐条件を与える①

上記のプログラムは、以下のように書き換えることができます。どちらも、実質的に同じ機能を提供します。

%reset -f
import random

for i in range(5):

  dice_num = random.randint(1,6)
  print(f'サイコロで{dice_num}がでたので「',end='')
  
  if dice_num >= 5 :  # 5以上ならば…。記号が「>」ではなく「>=」に注意
    print('あたり',end='')
  else : 
    print('はずれ',end='')
  
  print('」です。')

プログラムは 自然言語 (日本語) で与えられた手順どおりに実装する のではなく(アタマを柔らかくして)必要に応じて手順やロジックを工夫して実装することが重要です(ときには求められます)。

4.1.1 演習③

\(25\%\)の確率で「R」、\(75\%\)の確率で「N」という文字列を出力するプログラムを作成せよ (次のプログラムに追記する形で作成せよ)。

%reset -f
import random as r
for i in range(15): # 15連ガチャ
  n = r.random() # 0.0以上1.0未満の実数値
  print(f'n={n:.3f}') # 乱数確認用(あとでコメントアウトする)

4.2 「等しい」という条件の与え方

比較演算子 には大小関係を評価する > >= < <= の他に、「等しい」を評価する == が使用できます。ここで、= ではなく == であることに強く注意してください

実例で考えてみましょう。次のプログラムは、サイコロを投げて3の目がでたときだけ「あたりの処理」をするものです。実際に実行して、確認してみてください。

%reset -f
import random
dice_num = random.randint(1,6)
print(f'サイコロで{dice_num}がでました。')
if dice_num == 3: #「=」ではなく「==」であることに注意
  print('やったね!!')
print('また挑戦してね。') 
# ↑インデントしないことで、if文が抜けたあとの処理であることを与えている

このプログラムから分かるように、if文 (条件分岐構文) では必ずしも else節を必要としません

なお、比較演算子の「等しい (==) 」は 数値だけではなく 文字列に対して使用できます。以下に例を示します。

# 元ネタ FC版DQ3
%reset -f
name = input('名前を入力してください。')
if name == 'ロト' :
  print('その名前は使用できません。')
else :
  print(f'''「おはよう、{name}
もう あさですよ。
きょうは とても たいせつなひ。
{name} が はじめて おしろに いくひ だったでしょ。」''')

以前に助言したように「教わったこと」を教わった範囲で無条件に受け入れて終わるのではなく好奇心や興味関心を持って、発展的に「試す」「調べる」「考える」に取り組めるように なってください。

例えば、ここでは次のような「疑問」や「興味」が湧いて、それに対して「試す」「調べる++」「考える**」ができるようになってください。そういった習慣や思考回路になった人が、最終的に (4年生になる頃に) 高いプログラミングを身に付けることができます。

三連引用符

'''''' で囲んだ三連引用符を使用すると、長文や改行付の文字列リテラルの記述が簡単になります。三連引用符のなかでは、改行やスペースがそのまま有効になります。

次のように変数に格納して使用することもできます。

# 元ネタ FC版FF1
%reset -f
msg = '''このせかいは あんこくにつつまれている
かぜはやみ うみはあれ
だいちはくさっていく
しかし ひとびとは1つのよげんをしんじ
それをまっていた
このよ あんこくにそまりしとき
4にんのひかりのせんし あらわれん
ながいぼうけんのすえ
4にんのわかものがこのちにたどりついた
そしてそのてには
それぞれクリスタルがにぎられていた'''
print(msg)

4.3 「等しくない」という条件の与え方

等しくない」は != という比較演算子によって評価することができます。

4.4 アタマを柔らかくして分岐条件を与える②

条件を満たすときは何もせず、条件を満たさないときだけ何かをしたいという処理はどのように記述すればよいか考えてみてください。

例えば「サイコロを投げて6の目がでたときは何もせず、それ以外のときは「はずれ」を表示する」という処理を考えます。

まず、はじめに思い浮かぶのは、次のようなプログラムになると思います。実際に実行して結果を確認してください。

%reset -f
import random
dice_num = random.randint(1,6)
if dice_num == 6:
  # 何も処理しない
else:
  print('はずれ')

実行するとエラーとなることが確認できたと思います (実行以前の段階で赤の波線が表示されていることにも気づきましたか?)

Pythonでは、文法上、何も処理しないブロック (文が1つも存在しないブロック) を構成することは禁止されています。ブロック内では、(コメント以外に) 必ず何らかの文を書く必要があります。そこで、何もしない文として pass が存在します。

上記のプログラムは次のように書き換えることで実行可能となります。

%reset -f
import random
dice_num = random.randint(1,6)
if dice_num == 6:
  pass # 何も処理しない。
else:
  print('はずれ')

しかし、pass を使用しなくとも、次のように書き換えることで、より短いコードで同じ処理を実現することができます。繰返しになりますが、プログラムは 自然言語 (日本語) で与えられた手順どおりに実装する のではなく(アタマを柔らかくして)必要に応じて手順やロジックを工夫して実装することが重要です。

%reset -f
import random
dice_num = random.randint(1,6)
if dice_num != 6:
  print('はずれ')

4.5 elif節

elif は、次のような処理をしたい場合に利用します。

%reset -f
score = 85
print(f'試験が {score} 点だったので「',end='')
if score >= 90 :
  print('秀',end='')
elif score >= 80 :
  print('優',end='')
elif score >= 70 :
  print('良',end='')
elif score >= 60 :
  print('可',end='')
else :
  print('不可',end='')
print(f'」の評価です。')

上記のプログラム (elifを利用) と下記のプログラム (ifを利用) では処理の流れが変わってきます。実行時に、どのように違いがでるのかについて考えてみてください

%reset -f
score = 85
print(f'試験が {score} 点だったので「',end='')
if score >= 90 :
  print('秀',end='')
if score >= 80 :
  print('優',end='')
if score >= 70 :
  print('良',end='')
if score >= 60 :
  print('可',end='')
else :
  print('不可',end='')
print(f'」の評価です。')

4.5.1 演習④

\(10\%\)の確率で「SR」、\(25\%\)の確率で「R」、\(65\%\)の確率で「N」という文字列を出力するプログラムを作成せよ (次のプログラムに追記する形で作成せよ)。

%reset -f
import random as r
for i in range(15): # 15連ガチャ
  n = r.random() # 0.0以上1.0未満の実数値
  print(f'n={n:.3f}') # 乱数確認用(あとでコメントアウトする)

4.6 条件分岐構文のネスト

ネスト とは 入れ子 とも呼ばれるもので、条件分岐構文のなかに、さらに条件分岐構文を構成することを意味します。

基本的には何階層にも入れ子にすることができますが、深いネストを構築すると 可読性や保守性が著しく低下 します。深いネストが構成されないようにプログラムを工夫してください。

%reset -f
Ax = 5
Ay = -3
print('点Aは第',end='')
if Ay > 0 :
  if Ax > 0 :
    print('1',end='')
  else :
    print('2',end='')   
else :
  if Ax < 0 :
    print('3',end='')
  else :
    print('4',end='')   
print('象限に存在します。')

elif 節を使うことで 深いネストが構成されることを回避すること ができます。先ほどの「秀・優・良・可・不可」の判定プログラムを elif使わずに構成すると、以下のように 深いネストが形成されてしまいます (可読性・保守性の低いプログラムになってしまうことが分かると思います)。

%reset -f
score = 45
print(f'試験が {score} 点だったので「',end='')
if score >= 90 :
  print('秀',end='')
else :
  if score >= 80 :
     print('優',end='')
  else :
    if score >= 70 :
      print('良',end='')
    else :
      if score >= 60 :
        print('可',end='')
      else :
        print('不可',end='')
print(f'」の評価です。')

4.7 AND条件

AND条件and 演算子を使って評価することができます。

%reset -f
x = 0
y = 0
if x==0 and y==0 :
  print('点Aは原点上に存在します')
else :
  print('点Aは原点以外に存在します')

上記と同じ処理をするプログラムは、次のように条件分岐構文のネストでも構成できます。ただし、可読性・保守性に問題があることを実感してください。

%reset -f
x = 1
y = 0
if x==0 :
  if y==0 :
    print('点Aは原点上に存在します')
else :
  print('点Aは原点以外に存在します')

and 演算子では、x==0 and y==0 and z==0 のように3つ以上の条件の AND をとることもできます。

4.8 OR条件

OR条件は or 演算子を使って評価することができます。

%reset -f
x = 1
y = 1
if x==0 or y==0 :
  print('点AはX軸上もしくはY軸上に存在します')
else :
  print('点AはX軸上にもY軸上にも存在しません')

4.9 数値範囲条件

Pythonでは、次のように 数値範囲の条件 を与えることができます。

%reset -f
temp = 20
if 15 <= temp <= 25 :
  print('正常です')
else :
  print('正常範囲外です')

他のプログラミング言語では、次のようにAND条件を使って上記のような条件の評価をします。

%reset -f
temp = 20
if temp >= 15 and temp <= 25 :
  print('正常です')
else :
  print('正常範囲外です')

4.9.1 演習⑤

XY平面上に存在する点Aの座標を与えたとき、その値に応じて次のいずれかの文章を出力するプログラムを作成せよ。可読性と保守性を意識しながら作成すること。

%reset -f
import random as r
for i in range(5):
  x = r.choice([-1,0,1])
  y = r.choice([-1,0,1])
  print(f'点A ({x},{y})は、')

4.10 条件式の正体

x > 5x==00<=x<=10 のような条件式は、次のように if文以外の場所 でも使うことができます。

%reset -f
x = 10

# x > 5 の評価結果を 変数var1 に格納
var1 = x > 5  

# x == 0 の評価結果を 変数var2 に格納
var2 = x == 0  

# 変数に格納された値を出力
print(f'var1={var1}, var2={var2}')

上記の実行結果から分かるように、比較演算子を使った条件式は、True または False という bool型 の値になります。

bool型 は、True または False の2値から構成される値です。int型 (整数型) や float型 (浮動小数点数型) と同様に変数に格納 (バインド) することができます。

そのため、次のように利用することもできます。

%reset -f
x = -5
y = 12
z = 80
c1 = -10 <= x <= 10 # 条件式の評価結果をc1に格納
c2 = -15 <= y <= 15 #  〃 をc2に格納
c3 = -20 <= z <= 20 #  〃 をc3に格納
print('点Aは指定の空間内に',end='')
if c1 and c2 and c3 :
  print('存在します。')
else :
  print('存在しません。')

変数に格納せずに、if に直接的に条件を与えると次のようになります。

%reset -f
x = -5
y = 12
z = 80
print('点Aは指定の空間内に',end='')
if -10 <= x <= 10 and -15 <= y <= 15 and -20 <= z <= 20 :
  print('存在します。')
else :
  print('存在しません。')

5 標準入力からの書式付き入力の対応

詳しいことは後日解説しますが、標準入力からスペース区切りの文字を受け取って、内部で数値として扱いたい場合は、次のコードで処理することができます。

%reset -f
s = input() # スペース区切りで数値入力「12.5 -37.5 0.71」
a, b, c = map(float, s.split())

print(a)
print(b)
print(c)

6 課題 (宿題)

GoogleClassroomに提示したリンクからTechFULのセッション「PG1-課題03」に参加し、問題に取り組んでください。