Python:リストの内包表記
「Python コードレシピ集」(黒住敬之著 技術評論社)をぱらぱらと見ていたら,レシピ062にリスト内包表記があった。載っている例は,リスト list1 の各要素を2倍した list2 を作るもので,
list1 = [1,2,3]
list2 = []
for val in list1:
list2.append(val * 2)
print(list2)
を,内包表記を使うと
list1 = [1,2,3]
list2 = [val * 2 for val in list1]
print(list2)
とできる,という話。
これを読んで思い出したことがある。少し前に,Twitterでこんなやりとりをした。
Maehara さんのコードでは,0と1を反転するのに,まず 0 / 1 をブール値の代用として not で反転し,それを int で整数化している。筆者のコードでは,単に 1 から引くだけだ。 1 - y で,y が 0 なら 1,1 なら 0 になる。これは情報の授業でもやったもの。abs(y-1) という手もある。Cindyscriptなら絶対値記号が使えるので,|y-1| でよい。
最近思ったのは,それだったら,リストのコピーをするのに内包表記でいけるか,ということだった。その時にはうまくいかなかったが,もう一度やってみた。
リストのコピーは
list1 = [1,2,3]
list2 = list1
ではうまくいかない。リストはミュータブルだからだ。コピーしたつもりでも,list2 の要素を書き換えると,もとの list1 の要素も書き変わってしまう。
そこで,リストのコピーには,copy モジュールを import して,copy または deepcopy (2次元のとき)を使うのだが,そんなことをしなくても 内包表記でできないだろうか。
まず,1次元のリスト。
できた。
では,2次元では?
だめだ。list2 を変更したら,list1 まで書き変わっている。
では,Twitterでのやりとりのようにしたら?
できた。めでたしめでたし。
しかし,高校の授業ではリストの内包表記は教えない。面倒でも,for ループで append する。まず,理解できる生徒が少ないだろうし,共通テストでは絶対に出ないからだ。for ループを習熟する方がいい。
教材を作るのに,裏方として使うのはいいだろうな。1行で済むから。
ところで,「Python コードレシピ集」に載っている例だが,各要素を2倍するんだったら, 2 * list1 でいいか,と思うとそうはいかない。Python のリスト処理では,つぎのようになる。
ベクトルや行列の,スカラーをかけるときのようにしたければ,Numpy を使うことになる。
Cindyscriptでは,リストはそのまま線形代数で使える(Numpy の array と同じ)ので,次のようにできる。
Cindyscriptのリストに慣れていると,Pythonのリストは面倒でしょうがない。append (後方追加)はあっても prepend(前方追加)はないし,A = B でコピーできないし。ま,しょうがないか。
なんてことをやっているうちに,面白いことを見つけた。np.array はミュータブル? つまり,先程の例で,list2 = 2 * list1 で,各要素を2倍したリストを作った。じゃあ,list2 = list1 でコピーができる?
やっぱりだめだよね。
でもさ,こうすりゃできちゃうんだね。
2次元(Numpy では行列になる)でもできちゃう。
こういうのを Tips というべきか,裏技というべきか,好奇心を刺激することは確かだけど(プログラミングの楽しみの一つ) なんなんでしょね,Pythonって。