本記事は下書きリストから掘り出された個人的アーカイヴの1つです. 公開時期と執筆時期の間には,ある程度の開きがあります. |
オライリー本「退屈なことはPythonにやらせよう 初版第1刷」の各演習プロジェクトを自分なりに解いたものです.
かなり前に書いたやつです.下書きリストに残ってたのですが,誰かの役に立つかも知れないので,一応公開しておきます
ルール
演習時に使用する文法等は,基本的にその演習ページまでの内容に沿って書くものとしています.
例えば「演習の時点でfor文は説明されたけれど,while文と累算代入演算子は説明されていない」という場合,
x = 100
y = 0
while y > -131071:
x -= 1
y = (x ** 3) - (3 * x ** 2) + (x - 1)
print('when x=' + str(x) + ', y=' + str(y))
と書きたいところを
x = 100
y = 0
for i in range(10000): # edited
x = x - 1 # edited
y = (x ** 3) - (3 * x ** 2) + (x - 1)
if y <= -131071: # inserted
break # inserted
print('when x=' + str(x) + ', y=' + str(y))
と書く,といった具合です.
演習【3.11.1】コラッツ数列 (p.78)
■ 要件
-
number
というパラメータを1つ持つcollatz()
という名前の関数を書く -
number
が偶数なら,collatz()
はnumber / 2
を戻り値にする -
number
が奇数なら,collatz()
は3 * number + 1
を戻り値にする - ユーザーに整数を入力してもらい,その数を
collatz()
に渡して呼び出す -
collatz()
が1を返すまで,その戻り値をcollatz()
自身に渡し,呼び出し続ける - 入力を促す文章,入力値,collatz()の戻り値,を表示する
e.g.
■ 実装例
def collatz(number): # まず関数を定義
if number % 2 == 0: # 入力が偶数なら実行
number /= 2 # number = number / 2 を累算代入演算子で
else: # 入力が奇数(number % 2 != 0)なら実行
number = 3 * number + 1 # そのまま
return int(number) # 2で割ったときにfloat型になったのをint型に直す
print('input a number: ') # ここから実行される
n = int(input()) # 入力の受け付け+文字列から整数に変換
while n != 1: # 入力が1か,collatz()の返り値が1になるまでループ
n = collatz(n) # collatz()自身に値を返す
print(n) # 毎回返り値を表示させる
input() # コマンドプロンプトを閉じさせない(Windows)
# 空行(lintを静かにさせる)
最後のinput()
は,.pyファイルを開いて実行させるときのTipsです.(input()があれば,入力があるまで,プログラムが一時停止状態になる-->Enterを押すと,input()の行が実行されますが,値を返す先がないので,プログラムへの影響なしに,次の行へ移り,そのままプログラムが終了します)
IDLEなどの即時実行できる環境がある人は気にしなくても大丈夫です.
演習【3.11.2】入力の妥当性検証(p.78)
■ 要件(3.11.1への追加)
- ユーザーの入力が整数でない場合を判定するよう,try - except文を追加
- 尚,int()関数に文字列を渡すと,ValueError例外が起こる
- except節では,整数値の入力を促す文章を表示する
■ 実装例
def collatz(number):
if number % 2 == 0:
number /= 2
else:
number = 3 * number + 1
return int(number)
print('input a number: ')
while True: #常にTrue: breakが出るまで無限ループ
try:
n = int(input()) #ここでエラーが出たら,即時except節へ(ループする)
break #エラーが出なかったら,ループを抜ける
except ValueError:
print('input a integer!')#ループ終端なので,実行後自動でcontinue(while文の先頭へ)
while n != 1:
n = collatz(n)
print(n)
input() # PAUSE
不正な入力がないかをチェックするためのtry
&except
を,while
で囲み,エラーがなくなるまでループするようにしました.最初から入力が正常なら1回目で検証用ループを脱出(break
)します.
演習【4.10.1】カンマ付け (p.107)
■ 要件
-
spam = ['apple', 'bananas', 'tofu', 'cats']
のようなリストがあるとき, -
'apple, bananas, tofu and cats'
と返すような関数 - 関数には,任意のリスト値を渡しても良いようにする
■ 実装例
def f(arg): # display like [0], [1], ..., [-2] and [-1]
a = len(arg) # あとでループ幅に使うために,受け取ったリストの長さを代入
o = '' # 返り値に使うstr型変数を用意
for i in range(a): # arg[0]からarg[-1]までを操作
if i == (a - 1): # 最後の入力分
o += arg[i] # --> そのまま出力文字列に追加
elif i == (a - 2): # 最後から2番目の入力分
o += arg[i] + ' and ' # --> andを付けて出力文字列に追加
else: # 最後から3番目までは
o += arg[i] + ', ' # --> カンマを付ける
return o
print('Input something and press "Enter" to continue\n\
or input "end" if the list is over.')
spam = [] # 入力データを受け取るリストを用意
while True:
print('\n' + '- input: ', end='')
w = input()
if w == 'end': # endが入力されたら,リストの追加をやめる
print('\nThe list you made: ' + str(spam) + '\n')
break # 作成したリストを表示したあと,ループから抜ける
spam.append(w) # 随時,入力をリストに追加
print('\n--> ' + f(spam)) # 関数f()にspamを渡し,返り値を表示させる
input() # PAUSE
f(arg)のif文とelif文は入れ替わっても問題ないですが,if文が,処理を上から順番に,飛ばさずに実行するということを踏まえると,「arg[-1]
->arg[-2]
->その他」の順番が気持ちいいです.
入力が1単語のときはそれをそのまま返し,2単語だけのときはA and Bの形で表示するようになっています.
ちなみに,\n
は改行文字です.時々出てくるend=''
は,出力文字列の後ろに何もつけない指定をしています(デフォルトだと改行が入る).あと', '
とか' and '
というように,空白文字を前後につけたり...sep=','
を今回使わなかったのは,問題文にapple, bananas, tofu and cats
ではなく,'apple, bananas, tofu and cats'
(:1つの文字列)を返せと書いてあったからです.
関数の内部状態が分かるように,関数内に適宜input()
を入れたものがこちら:
', '
や' and '
が適宜挿入されている流れが分かると思います.
演習【4.10.2】文字絵グリッド (p.107)
■ 要件
■ 実装例
grid = [['_', '_', '_', '_', '_', '_'],
['_', '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', '0', '_', '_', '_'],
['_', '_', '_', '_', '_', '_']]
i = 0 #---------┐
for j in range(len(grid[i])): # |
for n in range(len(grid)): #---┐ |
print(grid[n][j], end='') # ├※1 ├※2
print('\n') #---┘ |
i += 1 #---------┘
input() # PAUSE
grid[i][j]とおき,
※1の発想...
-
grid[0][j]
~grid[-1][j]
というふうに,
j
を固定し,i
を動かすループが必要 - (
i
を動かす=行のインデックスを回す) -
len(grid)
をrange()に入れたfor文を回す:
j
は固定(外ループに依存)しつつ,i
を回す(グリッドの行数分,縦移動) - 横移動なしで縦移動し,一番したについたら,改行文字を入れる
※2の発想...
-
i
は固定(外ループ?に依存)しつつ,j
を回す(グリッドの列数分,横移動) -
i
も回さないといけないけど,for i in range(len(grid))
を一番外のループに書くと,同じようなループが3重になって見た目がすっきりしないので,j
ループ内のカウンタi += 1
でi
を回し,その外に初期値i = 0
を置く
というように,内側から順番に考えるとやりやすかったです.
休憩を入れてから取り組むとスムーズに解けました.詰んだときは,とりあえず別のことをしましょう!
今回は以上です.
気が向いたら続き書きます