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

2024年05月31日(金)1・2時限

1 準備と講義概要

2 前回の復習 ( 12分)

繰返し構文 (for 構文) と ColabTurtle ライブラリを使用して、次のような六角形を描画してください。コードセルの実行には Ctrl + Enter のショートカットを利用する習慣をつけてください。

img
%reset -f
import ColabTurtle.Turtle as t
t.initializeTurtle(initial_speed=13,initial_window_size=(400,400))

t.shape(shape='circle') # ペン先を「亀」から「円」に設定
t.bgcolor(0,0,0);       # 背景色を「黒」に設定 bg:background 
t.color(0,255,0)        # 線色を「緑」に設定
t.width(10)             # ペンの太さを「10」に設定

# ▼▼▼ 以降を書き替える ▼▼▼ 
t.up()          # ペンを上げる
t.goto(50,350)  # X=50, Y=350 に移動
t.face(0)       # 0度方向 (3時方向) を向く
t.down()        # ペンを下げる

for i in range(3): # 3回の繰返し
  t.forward(50)    # 50px 前進
  t.left(90)       # 90度の右回転

3 課題02に関する補足

前回講義で出題した課題02に関連する補足です。

3.1 ColabTurtleのカメの移動速度

複雑な図形を描く場合は、カメ(カーソル)の移動速度の初期値 (initial_speed) を最大値である 13 にすると短時間で描画処理が終了します。開発中は 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) のように繰返し回数を調整します。なお、慣れないうちは、適宜、括弧を使って range((360//n)+1) のように記述すると分かりやすいかもしれません。

4 インデントとブロック

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

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

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

4.1 演習1 ( 8分)

次の各プログラムを実行したとき どのような出力 (実行時エラーを含む) が得られるか を予測してください(=脳内でプログラム動作をシミュレートせよ)。また、実際に 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')

5 GoogleColabのテキストセルでの数式記述

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

img

例えば、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_{k}\pm b_{k})=\sum_{k=1}^{n}(a_{k})\pm \sum_{k=1}^{n}(b_{k}) $$

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

\[ \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_{k}\pm b_{k})=\sum_{k=1}^{n}(a_{k})\pm \sum_{k=1}^{n}(b_{k}) \]

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

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

ここで $a_{n}$ は・・・・

img

6 Colabのフォーム機能

Google Colab の環境では「フォーム」という機能を使ってGUIライクに入力を与えることもできます。

次のコードを実行して、その動作を確認してみてください。変数に値を代入している行のコメントで #@param ... のように書いているのがポイントです。

%reset -f
name = '山田'      #@param {type:"string"}
height = 172.5    #@param {type:"number"}
age = 10          #@param {type:"slider", min:10, max:80, step:1}
print(f'{name}さんの身長は{height}で、現在年齢は{age}歳で、1年後は{age+1}歳になっています')

この フォーム の機能は、GoogleColab環境でのみ使用ができます (Python固有の機能ではありません)。詳細については 公式ページ を参考にしてください (ドロップダウンリストや日付選択コンポーネントなど、様々なフォームが紹介されています)。

セルを選択して、三点リーダを選択するとフォームに関する オプション設定 ができます。

img

6.1 フォームの利用例

次のコードを実行して動作を確認してください。#@markdown に続けて、数式を含めたマークダウン記法が利用できていることが分かると思います。

%reset -f
#@title **物理学習支援ツール**
#@markdown 時刻 $0$ において速度 $v$ ($\mathrm{m/s}$) の物体が、
#@markdown 一定の加速度 $a$ ($\mathrm{m/s^{2}}$) を保ちながら
#@markdown $t$ 秒間の間に進む 距離 $x$($\mathrm{m}$) を求めます。

#@markdown 速度 $v$ ($\mathrm{m/s}$) を入力してください 
v = 1.5    #@param {type:"number"}
#@markdown 加速度 $a$ ($\mathrm{m/s^{2}}$) を入力してください 
a = 2    #@param {type:"number"}
#@markdown 走行時間 $t$ ($\mathrm{s}$) を入力してください 
t = 4    #@param {type:"number"}

x = v*t + 0.5*a*t**2
print(f't=0 から t={t} に走行した距離は {x:.2f} (m) です。')

7 条件分岐構文

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

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

実例で考えてみましょう。次のプログラムは、サイコロを投げて「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言語では囲まない ので注意してください。

7.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('」です。')

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

7.1.1 演習2 ( 6分)

\(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}') # 乱数確認用(あとでコメントアウトする)

注意 : random() は、randint() とは違って、random(0, 1) のように「引数」を与えずに使用します。もし、\(0.0\) 以上 \(20.0\) 未満の実数を得たい場合は r.random() * 20 のようにします。また、\(-10.0\) 以上 \(のようにします。10.0\) 未満の実数を得たい場合は r.random() * 20 - 10 のようにします。

7.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年生になる頃に) 高いプログラミングを身に付けることができます。

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

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

7.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('はずれ')

7.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'」の評価です。')

7.5.1 演習3 ( 10分)

15連ガチャを実装するとします。\(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}') # 乱数確認用(あとでコメントアウトする)

7.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'」の評価です。')

7.7 AND条件

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

%reset -f
x = 0  # 点AのX座標
y = 0  # 点AのY座標
if x==0 and y==0 :  #「and演算子」を利用
  print('点Aは原点上に存在します')
else :
  print('点Aは原点以外に存在します')

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

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

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

フォーム機能の活用

先に紹介したフォーム機能を利用することで、次のように操作性に優れたプログラムにすることができます。@title{ run: "auto" } を含めることで、スライダーを変更すると 自動実行 されるようになります。

img
# @title 座標の判定 { run: "auto" }
%reset -f
x = 0  #@param {type:"slider", min:-5, max:5, step:0.5}
y = 0  #@param {type:"slider", min:-5, max:5, step:0.5}
if x==0 and y==0 :  #「and演算子」を利用
  print('点Aは原点上に存在します')
else :
  print('点Aは原点以外に存在します')

自動実行が機能しない場合は、初回のみ Ctrl + Enter で手動実行してみてください。その後、スライダーを移動するとプログラムが自動実行されます。

7.8 OR条件

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

%reset -f
x = 1
y = 1
if x==0 or y==0 :  #「or演算子」を利用
  print('点AはX軸上もしくはY軸上に存在します')
else :
  print('点AはX軸上にもY軸上にも存在しません')

なお、文字列の等価性の比較は、card を変数とすれば card == 'UR' のようにします。

以下に、文字列の比較と、ここまでに学習した内容の応用 (単なる組み合わせ) を示します。

%reset -f
import random 
import time
from IPython.display import clear_output

cards = ['UR','SSR','SR','S'] # 排出リスト
card = random.choice(cards)   # リストからランダムに1枚を取得

# 演出
for i in range(5):
  print('.', end='')
  time.sleep(0.5)       # 0.5秒
clear_output()          # 出力をクリア

# 指定のカードを得たときに追加メッセージ
if card == 'UR' or card == 'SSR' :
  print(f'🎉おめでとうございます!', end='')

# 共通メッセージ
print(f'「{card}」をゲット!')

第04行目第13行目clear_output のみ新規の要素です。random.choice第02回講義time.sleep第01回講義 で学習済みです。

7.9 数値範囲条件

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

%reset -f
temp = 20 # 温度 (Temperature)
if 15 <= temp <= 25 : # tempが15以上25以下のとき「真」となる
  print('正常です')
else :
  print('正常範囲外です')

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

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

7.9.1 演習4 ( 7分)

数値範囲条件の与え方1.py について、フォーム機能を利用して「温度temp」がスライダーで与えられるようにしてください。また、スライダーが変更されると自動実行されるようにしてください。

7.9.2 演習5 ( 10分)

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})は、')

7.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('存在しません。')

8 宿題

次回講義、6月4日(火) に「小テスト」を実施します。今回の授業で扱った「条件分岐構文」に関する Pythonプログラム を正しく記述できるようになっておいてください。