Python は映画やアニメの製作でもよく使われている: Python For Feature Film
break
文はループから抜け出すのに使える。
for
文は便利だ。
i = 0 while i < 10: print(i) i = i + 1
for i in range(10): print(i)
a[0]
) を削除して
a = [9,4,7,0,0]
となるようにしたい。
??? の中を埋めよ。
a = [5,9,4,7,0] i = 0 while i < 4: a[???] = a[???] i = i + 1
a = [5,5,9,4,7]
となるようにしたい。が…
以下のプログラムを実行したとき
a
の中身がどうなるかを予測せよ。
a = [5,9,4,7,0] i = 0 while i < 4: a[i+1] = a[i] i = i + 1
a = [5,5,9,4,7]
となるようにしたい。
??? の中を埋めよ。
a = [5,9,4,7,0] i = ??? while ???: a[i+1] = a[i] i = ???
実際には、Python にはリストの途中の要素を削除したり 途中から挿入したりするためのもっと簡単な方法がある。 しかし、実際に内部で起きているのはこういうことである。
類似の問題: 0〜4までの番号がついた5個のイスが並んでいる。 0番目のイスをはずして残りを詰めるにはどうすればよいか?
Python の変数に格納されるデータは、 実はいくつかの「型 (type)」に分類されている:
3
, -15.7
など。
"abc"
など。
[4, 6, 4, 9]
など。["月", "火", "水", "木", "金", "土", "日"]
[ [9,6,3], [8,5,2], [7,4,1] ]
True
と
False
の2種類の値しかない。)
+
演算子の意味は、計算対象のデータ型によって異なる。
print(123+456) # 整数 + 整数: 579 print("123"+"456") # 文字列 + 文字列: "123456" print(123+"456") # 整数 + 文字列: エラー!
型が異なるデータの計算は意味をなさないので、ふつうはエラーが発生する。
"abc" - 5
… エラー!
[3,1,4] + "moo"
… エラー!
int(文字列)
… 文字列を整数として解釈する。
ord(文字列)
… 1文字の文字コード (Unicode) を整数として返す。
a = 123 # a は整数の 123 b = int("123") # b は整数の 123 print(a+b) # 整数+整数: 246
print(ord("A")) # "A"の文字コード: 65 print(ord("あ")) # 「あ」の文字コード: 12354
s = input("string:") for i in range(len(s)): # i は 0〜(文字数-1) まで変化する。 print(ord(s[i])) # i番目の文字コードを表示。
本来、Pythonのinput
関数は入力された 文字列 (string) を返す。
s = input("string:") # s は文字列型
print(s)
ここに int()
関数を適用することで、
文字列を整数型として利用できる。
s = input("number:")
x = int(s) # x は文字列型
print(x)
str(数値)
… 整数を文字列として表す。int()
の逆。
chr(数値)
… 整数で表される文字コードの文字を返す。ord()
の逆。
a = 123 # a は整数の 123 b = str(a) # b は文字列の "123" print(b+"0") # 文字列+文字列: 1230
a = 9829 b = chr(a) # b は Unicode 9829番 で表される文字 print("I"+b+"NY") # I♥NY
chr()
関数に与える文字コードは
Unicode で定義されている。
for i in range(26): # 26回繰り返す。 print(chr(65+i)) # (65+i) で表されるUnicodeの文字を表示する。
このプログラムを実行すると、A から Z までの文字が表示される。
まとめると、以下のような関係がなりたつ:
文字 → 数値 | 数値 → 文字 | |
---|---|---|
文字列 | int(x) | str(x) |
文字 | chr(x) | ord(x) |
f"〜"
と "〜"
の違い
じつは、print
関数の機能は
print(文字列) # 文字列を表示する。
などの書き方は、以下のようにもできる:print("hello") print(f"x={x}")
s = "hello" # s は文字列型 print(s) # s の内容を表示 t = f"x={x}" # t は文字列型 print(t) # t の内容を表示
さらに、
f"x={x}"
は、
文字列の連結と str(x)
関数を
組み合わせたものと同じである:
t = "x=" + str(x)
print(...)
、input(...)
のような部分。
引数 (ひきすう, arguments) をとり、値を返す。
print(...)
… 文字列を表示する。
input(...)
… 文字列を入力する。
int(...)
… 文字列を数値に変換。
str(...)
… 数値を文字列に変換。
ord(...)
… 文字を文字コード(数値)に変換。
chr(...)
… 文字コード(数値)を文字に変換。
time.sleep(...)
… 一定時間だけ待つ。 (アニメーションで使用)
注意:
if
文, while
文, for
文などは
「文」であって関数ではない。
abs
: 絶対値を求める。
>>> abs(2) 2 >>> abs(-11) 11
pow
: べき乗を求める。
>>> pow(3, 4)
81
>>> pow(2, 0.5)
1.4142135623730951
>>> 3 ** 4
81 # **演算子を使っても同じ
sum
: リストの和を求める。
>>> a = [5,9,4,0] >>> sum(a) 18
>>> chr("a") Traceback (most recent call last): File "", line 1, in TypeError: an integer is required (got type str) エラー: chr関数には、数値型を与えなければならない。>>> sum(4) Traceback (most recent call last): File "", line 1, in TypeError: 'int' object is not iterable エラー: sum関数には、リスト型を与えなければならない。>>> input("x:", "y:") Traceback (most recent call last): File "", line 1, in TypeError: input expected at most 1 arguments, got 2 エラー: input関数は、値を1つだけしか与えてはいけない。
ターミナルで、プログラム (.py
ファイル) を書かずに、ただ
とだけ入力すると、Pythonが「対話モード」で起動する。 この場合、Pythonのプログラムを1行ずつ直接入力して実行することができる。 これは、ちょっとした動きを確かめたいときなどに便利だが、 複雑なプログラムは書けない。$ python
$ python Python 3.7.4 (default, Oct 4 2019, 06:57:26) [GCC 9.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> x=2 >>> y=3 >>> x+y >>> print(x,y) 2 3
Python では、自前の関数を新しく定義することができる。 新しく定義された関数は、従来からある関数とまったく同じように使える:
# 関数 plus(a, b) … aとbの和を求める。 def plus(a, b): return a + b x = 2 y = 3 z = plus(x, y) # zの値は 5。
プログラムが複雑になってくると、プログラムを全部一度に書くのではなく、 部分ごとに分けて書く、ということが必要になってくる。 関数はもともと数学の概念だが、Python における関数とは、 プログラムの処理の一部をおこなう部品である。 ここでは入力と出力をもった「ブラックボックス」に近い。
def 関数名(変数名1, 変数名2, ...): 関数の中身 ... return 関数が返す値
return
文を実行し、ある値を返す。
この値を関数の返り値 (あるいは戻り値) と呼ぶ。
変数名 = 関数名(値1, 値2, ...)
z = plus(x, y)
を実行したとき、
関数 plus()
の中で定義されているプログラムが実行される。
これを「関数の呼び出し」という。
x
と y
の値は
それぞれ plus
内の変数 a
と b
に
コピーされて実行される。これを「関数に値を渡す」という。
return
文が実行されると
その関数は終了し、return
の直後の値が
変数 z
の値に代入される。
これは「関数から値を受けとる」ことになる。
図示すると、以下のようになる:
def plus(a, b): return a + b x = 2 y = 3 z = plus(x, y)
plus(x, y)
を呼び出したとき、
関数の中で変数 a
, b
に入る値は何か?
a
, b
に入る値は何か?
p = 1 z = plus(3, p)
def avg(a, b):
c = (a+b)/2
return c
print(avg(3, 5)) # 4
関数の中で別の変数を使うこともできる。
print(1 + avg(3, 5)) # 5 print(avg(avg(3, 5), 8)) # 6
関数の返り値を計算したり、それをさらに関数を渡すこともできる。
def sign(x): if x < 0: return "negative" elif 0 < x: return "positive" else: return "zero" print(sign(-3)) # "negative"と表示される。 print(sign(4)) # "positive"と表示される。関数の中で
if
文や while
文を使うこともできる。
関数が返すデータの型は、数値以外のものでもOK。
また、関数の中に return
文を複数書いてもよい。
def greet(name):
print(f"Good morning, {name}")
return
greet("euske") # "Good morning, euske"と表示される。
(数学ではありえない) 値を返さない関数というものも存在する。
この場合、関数の中で print
文を実行させることで
画面にものを表示させることのみが目的となる。
def constant(): return 1279 print(constant()) # 1279 print(constant()) # 1279 print(constant()) # 1279
引数をとらない関数は、何回実行しても同じ結果となる (当然)。
# 与えられた画面にフィットする画像の大きさを計算する。 def fitsize(x, y, w, h): if w*y < x*h: return [w, y*w/x] # 横フィットの場合 else: return [x*h/y, h] # 縦フィットの場合 print(fitsize(20,10,100,100)) # [100, 50] print(fitsize(20,10,400,100)) # [200, 100]
リストを返すこともできる。
# aとbを足す def plus(a, b): return a + b # xに2を足す def add2(x): # 定義した関数を使っている。 return plus(x, 2) print(add2(3)) # 5
一度定義した関数は他の関数と同じように使えるので、 さらに別の関数からも使える。
def navi(): print("Hey!") return print("Listen!") # 表示されない。 return navi() # "Hey!" のみが表示される。
関数の実行は、最初の return
文を実行した時点で終了する。
注意: 関数の内と外で同じ名前の変数を使っている場合、 関数内の変数は、外の変数とは別物として扱われる:
def plus(x, y): print(f"x={x}") # ??? print(f"y={y}") # ??? z = x + y print(f"z={z}") # ??? return z x = 100 y = 200 z = 300 a = 2 b = 3 print(plus(a,b)) # 5 print(f"x={x}") # ??? print(f"y={y}") # ??? print(f"z={z}") # ???
これは、関数がプログラムの他の部分とは完全に独立した プログラムと考えられているためである。 関数の外でどんな名前の変数を使っていようが、 関数の実行結果が影響を受けるべきではないからである。 関数内で使われる変数には、引数の値が一時的にコピーされるだけと 考えるのがよい。
x
, y
, z
の値が
それぞれどう表示されるかを確認せよ。
average
を完成させよ:
def average(a): x = 0 for i in range(len(a)): x = x + ??? return ??? print(average([1,2,3])) # 2 print(average([10,15,17,20])) # 15.5
bar
を書け。
bar(3) # "***" を表示する bar(5) # "*****" を表示する
bar
を使って、
与えられた大きさの三角形を表示する関数 triangle
を書け。
# 以下のように表示する: # * # ** # *** triangle(3) # 以下のように表示する: # * # ** # *** # **** # ***** triangle(5)
演習の合間に、11月1日に提出したアニメーション・コンテストの 投票をおこなう。
以下のページから、1a クラスの参加作品一覧が見られる。
いくつかのアニメーションは1度目の再生ではブラウザの反応が遅くなり、
うまく動かないことがある。おそらくこれは表示が速すぎてブラウザが
追いつかないためだと思われる (2度目の再生ではうまく動くことが多い)。
time.sleep(0.01)
は毎秒100コマという意味であり、
ほとんどのPCはこんな速度で描画を行うことはできない。
教訓: 自分のプログラムが自分のパソコンで動いたからといって、
世界中のパソコンで同じ条件で動くとは限らない。
投票は、以下のページからおこなう:
投票の結果、本授業の最後に優秀者を決定する。
「シーザー暗号」とは、ジュリアス・シーザー (Julius Caesar, ユリウス・カエサル) が 使ったと言われる暗号で、アルファベットの順番をずらして文を作成する方法である。 暗号を元にもどす (復号) ためには、順番を逆方向にずらせばよい。 このとき、ずらす回数 k をその暗号の鍵 (key) とよぶ。
HELLO!
→ IFMMP!
(k=1, 1つ先の文字を使った場合)
HELLO!
→ ROVVY!
(k=10, 10個先の文字を使った場合)
このような暗号は、Python のプログラムで簡単に作ることができる。 入力された文字が英文のアルファベットであると仮定して、 各文字コードに与えられた数 k を足せばよい:
# 与えられた文字列 text を k回先にずらす。 def caesar(text, k): # 結果を入れる文字列変数を準備する。 result = "" for i in range(len(text)): c1 = text[i] # i番目の文字を取得する。 n1 = ord(c1) # その文字コード。 n2 = n1 + k # k 回ずらした文字コード。 # ずらした文字を結果に追加する。 result = result + chr(n2) # できた文字列を返す。 return result print(caesar("HELLO", 1)) # "IFMMP" と表示する。 print(caesar("IFMMP", -1)) # "HELLO" と表示する。
HELLO
で k=1 の場合はよいのだが…
print(caesar("HEY!", 2))
# "JG[#" と表示される。
問題は、文字コードをずらしたときに
A〜Z の範囲を超えてしまうことである。
このような場合でもうまく動くように、以下の改良を加えよ:
%
を使うべし。
上のような簡単な英文のシーザー暗号は、すぐに解読することができる。 エドガー・アラン・ポーが 19世紀に発表した推理小説 「黄金虫」では、 このような暗号が使われていた (こちらは任意の文字が別の文字と対応するので、より難しい)。 暗号解析 (cryptoanalysis) とは、暗号の鍵がわからない状態で、 暗号文だけを頼りにもとの文を推測 (解読) することである。 暗号解析技術は、現在でも各国の軍事機関でさかんに研究されている。 cf. 暗号技術博物館
英文でもっともよく現れる文字は "e" であることから、 ある英文がシーザー暗号で書かれていると仮定すると、 暗号解析は以下のように行える:
E
になるように、シーザー暗号の鍵 k を決定する。
「暗号の中でもっとも多く現れている文字をさがす」処理は、 前々回にやった投票システムの課題と似ている。 つまり、暗号中の各文字をその文字に対する票と考え、 もっとも票の多かった文字を見つければよい。
def analyze(text): # 26文字分の投票結果を用意する。 votes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0] # 1文字ずつ調べる。 for i in range(len(text)): c = ord(text[i]) # i番目の文字の文字コードをとりだす。 # それが英大文字の範囲であれば、票を1増やす。??? ...# votes の中で、最も多く得票したものを見つける。 maxi = 0 for i in range(len(votes)):??? ...# この時点で、「maxi番目の文字」がもっとも多く現れることが判明。 # これが "E" を表すように、k の値を計算する。 k = ??? return k
bar
および関数triangle
を完成させよ。
以下のようなプログラムとして T2SCHOLA から提出すること。
# bar(n): n個のアスタリスクを表示する。 def bar(n): ... # triangle(h): 高さhの三角形を表示する。 def triangle(h): ... # inputで与えられた高さxの三角形を表示する。 x = int(input("height:")) triangle(x)
# 関数 caesar の定義 def caesar(text, k): ... # 関数 analyze の定義 def analyze(text): ... # 解読対象の暗号文。 cipher = ( "JXU ISYUDJYIJ XQI Q BEJ EV UNFUHYUDSU MYJX YWDEHQDS" "U QDT TEKRJ QDT KDSUHJQYDJO, QDT JXYI UNFUHYUDSU YI" " EV LUHO WHUQJ YCFEHJQDSU. MXUD Q ISYUDJYIJ TEUID’J" " ADEM JXU QDIMUH JE Q FHERBUC, XU YI YWDEHQDJ. MXUD" " XU XQI Q XKDSX QI JE MXQJ JXU HUIKBJ YI, XU YI KDS" "UHJQYD. QDT MXUD XU YI FHUJJO TQHD IKHU EV MXQJ JXU" " HUIKBJ YI WEYDW JE RU, XU YI IJYBB YD IECU TEKRJ. " "MU XQLU VEKDT YJ EV FQHQCEKDJ YCFEHJQDSU JXQJ YD EH" "TUH JE FHEWHUII MU CKIJ HUSEWDYPU EKH YWDEHQDSU QDT" " BUQLU HEEC VEH TEKRJ. ISYUDJYVYS ADEMBUTWU YI Q RE" "TO EV IJQJUCUDJI EV LQHOYDW TUWHUUI EV SUHJQYDJO — " "IECU CEIJ KDIKHU, IECU DUQHBO IKHU, RKJ DEDU QRIEBK" "JUBO SUHJQYD.") # 暗号解析をおこない、鍵の値 k を決定する。 k = analyze(cipher) # その値を使って暗号文を元にもどす。 text = caesar(cipher, -k) # 復号した文字列を表示する。 print(text)