これで点数が上がること間違いなし!
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)
演習の合間に、10月29日に提出したアニメーション・コンテストの 投票をおこなう。
選考基準:
投票は、以下のページからおこなう:
投票の結果、本授業の最後に優秀者を決定する。
「シーザー暗号」とは、ジュリアス・シーザー (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) とは、暗号の鍵がわからない状態で 暗号文だけからもとの文を推測 (解読) することである。 暗号解析技術は、現在でも各国の軍事機関でさかんに研究されている。
英文でもっともよく現れる文字は "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
本日の課題は 2つある。 小課題 7. のほうは難しいので、余力のある人のみ提出すればよい。
# 関数 caesar の定義 def caesar(text, k): ... # 関数 analyze の定義 def analyze(text): ... # 解読対象の暗号文。 cipher = ( "DOLU H ZJPLUAPZA KVLZU’A RUVD AOL HUZDLY AV H WYVI" "SLT, OL PZ PNUVYHUA. DOLU OL OHZ H OBUJO HZ AV DOH" "A AOL YLZBSA PZ, OL PZ BUJLYAHPU. HUK DOLU OL PZ W" "YLAAF KHYU ZBYL VM DOHA AOL YLZBSA PZ NVPUN AV IL," " OL PZ ZAPSS PU ZVTL KVBIA. DL OHCL MVBUK PA VM WH" "YHTVBUA PTWVYAHUJL AOHA PU VYKLY AV WYVNYLZZ DL TB" "ZA YLJVNUPGL VBY PNUVYHUJL HUK SLHCL YVVT MVY KVBI" "A. ZJPLUAPMPJ RUVDSLKNL PZ H IVKF VM ZAHALTLUAZ VM" " CHYFPUN KLNYLLZ VM JLYAHPUAF — ZVTL TVZA BUZBYL, " "ZVTL ULHYSF ZBYL, IBA UVUL HIZVSBALSF JLYAHPU.") # 暗号解析をおこない、鍵の値 k を決定する。 k = analysis(cipher) # その値を使って暗号文を元にもどす。 text = caesar(cipher, -k) # 復号した文字列を表示する。 print(text)