読者です 読者をやめる 読者になる 読者になる

exits

勉強記録

Pythonで最近好きなこと(1)

--2016.08.29 他のブログに書いていた記事をこちらに移しました

この記事はKobeUniv Advent Calendar 2015の15日目に書かれたものです. 執筆開始時点で15日になっちゃってますが気にしないのです.

ブログとしてもほとんど一発目の記事なのになんなのですが,私が最近好きなPythonの色々について書きます.

内包表記

とは

内包表記は配列に対して簡単なマッピングができる機能だと理解しています. 以下の例では配列aの各要素を全て2倍した配列を作ります.

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
>>> [i * 2 for i in a]
[0, 2, 4, 6, 8]

aの中の各要素iに対して次々とi*2を行っています.

もちろん,わざわざリストを作ってから入れる必要はありません.

>>> [i * 2 for i in xrange(5)]
[0, 2, 4, 6, 8]

上記の例だとrange(0, 5*2, 2)の方が効率がいいと思うのでありがたみがないですね・・・

内包表記を初めて目にしたのは,多重リストの宣言時です. この例ならばある程度,内包表記の必然性があると思います.でもnumpy.arrayでも使っとけばって言われる気もします. (ここの例がなんか違ったので変更しました 12/15)

>>> axis = 3
>>> seqlen = 2
>>> seq = [[0 for j in range(0)] for d in range(axis)]
>>> for i in xrange(axis):
...     seq[i].append(i + 1) # このへんは適当
...     seq[i].append(i + 4)
>>> seq
[[1, 4], [2, 5], [3, 6]]

[[]*axis]とやると同じ構造の多重リストにはなりますが,中のリストの内容が同期されてしまいます.中の[]が同じものになってしまうんですね.

卒論時は調べてふむふむとスルーしていたのですが,インターンでコードレビューを受けた時にちゃんと教えてもらって衝撃を受け,最近は意識して使ってみてます.不必要なところにも使っている気がするので今後の課題です.

if & ifelse

内包表記の楽しいところはifelseを埋め込めるところです.

>>> [i for i in xrange(10) if i%3 == 0]
[0, 3, 6, 9]

ifの場合は後ろにif文をひっつけます.

>>> ['fizz' if i%3 == 0 else i for i in xrange(10)]
['fizz', 1, 2, 'fizz', 4, 5, 'fizz', 7, 8, 'fizz']

if-elseの場合はややこしいですが,

(条件文を満たす時の出力) if (条件文) else (満たさない時の出力)

となっています.

最近内包表記を使って楽しかった例

  • 一文FizzzBuzz
# -*- coding: utf-8 -*-


def fizzbuzz1(size):
    '''普通のFizzBuzz
    '''
    fizzbuzz_list = []
    for i in xrange(1, size+1):
        out = ''
        if i % 3 == 0:
            out += 'Fizz'
        if i % 5 == 0:
            out += 'Buzz'
        if out == '':
            out += str(i)
        fizzbuzz_list.append(out)
    return fizzbuzz_list


def fizzbuzz2(size):
    '''一行FizzBuzz
    '''
    return ['Fizz' * (i % 3 == 0) + 'Buzz' * (i % 5 == 0)
            if i % 3 == 0 or i % 5 == 0 else str(i)
            for i in xrange(1, size+1)]


if __name__ == '__main__':
    size = 100
    print '\n'.join(fizzbuzz1(size))
    print '\n'.join(fizzbuzz2(size))

これは一部分ググりました,すみません. 条件文の返り値であるTrue/Falseが実は1/0として扱われているのを利用したものです.

  • ルンバの進行方向
def rumba(n):
    done = set()
    init_pos = (0, 0)
    dirs = ((0, 1), (1, 0), (-1, 0), (0, -1))
    print clean_new(n, init_pos, done)


def clean_new(n, pos, done, dirs):
    if n == 0:
        return 1
    new_done = set(done)
    new_done.add(pos)

    count = 0
    for new_dir in dirs:
        new_pos = tuple([p + nd for p, nd in zip(pos, new_dir)])
        if new_pos not in new_done:
            count += clean_new(n-1, new_pos, new_done, dirs)
    return count

if __name__ == '__main__':
    rumba(12)

ルンバが掃除をしていく経路数を求める,みたいなのを解いていた時のコードです. 最近は研究室でこういう問題を出してくれる人がいます.
競プロ勢に到底なれる気がしませんが,こういうのを解くのは楽しいです.

ここで内包表記を使う良い点は,ルンバの掃除が2次元に限られないところです. init_posとdirsを書き換えるだけで3次元でも4次元でも計算が可能です.
ぜひルンバには11次元のお掃除をしていただきたいところです. numpy使えとの声は無視します.

その他

  • ジェネレータとラムダ式についても書こうと思ってたのですが,ここまでで結構かかったので今度にします.
  • この記事のためにブラウザ版のJupyter Nodebookを使ってみようかと思いましたが,コピペするにしてもIn [1]:みたいな表示をどうするかがめんどくさかったので,今回は止めました.今後使ってみたいものの1つです.
  • Pythonicになりたい.