序文
なるべく短いコードで問題を解くのを競うことをcode golfといいますが、CheckIOのサイトでもコードの短さで点を競うgolf問題が増えてきました。この前の年末に日本人限定のcode golf問題などがあったのですが参加者が少ないこともあったのでPython golfのテクニックをお見せすることでcode golfの奥深い世界と面白さを知ってもらい、是非Code golfに挑戦して欲しいなと思います。
以下のテクニックはStack Exchangeのスレッドより引用しております。Python特有のテクニックが多く含まれますので他の言語への応用は難しいかもしれません。
また以下は自分が実際に使って重要度が高そうな順に書いてありますので、もっと多くのテクニックを見たい人はStack Exchangeの元スレッドを参照するのがいいと思います。
ifのブロック内は一つの文であれば一行でかける
if a>0:
print(a)
は一行で
if a>0: print(a)
と書けます。
if文をより短く一行で
if a<b: c=a
else: c=b
は
c = a if a<b else b
c = a<b and a or b
などに置き換えられます。
短絡評価を気にしない(a<bでもc=bが評価される)のであれば
c = [b,a][a<b]
と書き換えられ、これが一番短いです。
一行にすることによってlambda式やリストの内包表記、他の演算などとも組み合わせることができるので応用力のあるテクニックです。
なるべくlambdaを使う
def f(a,b):
if a<b: return a
else: return b
より
f=lambda a,b: a<b and a or b
と書けばdef や return等の分だけ文字数の節約になります。また先程のテクニック同様、リストの内包表記やmap()関数等との併用してより短くすることができるので一行で表せないか考えてみることが重要です。
range, enumerate 等の頻出する関数を短くする
for文を使う時によく用いるrangeやenumerate等の関数は一度短い変数に置き換えてから使うと2回以上使った時に文字数の節約になります。
使用前
for x in range(10):
for y in range(10): print(x, y)
使用後
r=range
for x in r(10):
for y in r(10): print(x, y)
この例では1文字の節約にしかなってませんが、以後rangeが出てくる毎に4文字の節約になります
また上記の例限定ですがr=range(10)
とすると更に短くできます。
比較演算子を少なくする
if a>1 and b>1 and 3>a and 5>b: print(a, b)
は
if 3>a>1<b<5: print(a, b)
と書けます。また以下の表現で
if a<b and c>d: print(a, b, c, d)
短絡評価を気にしない(a<b
の時でもc>d
が評価される)のであれば
if (a<b)*(c>d): print(a, b, c, d)
と書けます。and
を *
、 or
を +
に変更すると演算子の優先順位が変わるため括弧が必要な場合があったり、式評価の挙動が異なることに注意が必要ですが、場合によっては有効なテクニックです。
ブロックのスペース文字を節約する
if 1:
if 1:
\tpass
ここで\t
はタブ文字です。2段階目のインデントがスペースではなくタブ文字1つで済むので1文字節約になります。(但し、CheckIOの評価システムではこれは短くなったとみなされない気がします)
or, and 演算子の前のスペースを詰める
if a==1 and b==2: print(a,b)
は
if a==1and b==2: print(a,b)
と書くことができるので、スペース一文字の節約になります。ちりも積もれば山となることもあります。
n回繰り返しを短く書く
例えば5回繰り返したい時は
for i in range(5):
print(1)
と書くのが普通ですが、変数iをforループで使用しない場合
for i in[0]*5:
print(1)
と書けてスペースも含め4文字の節約になります。
ここ以外にもリストの掛け算表記は有効に使えることがあります。
更に後に述べるマルチステートメントのテクニックと組み合わせると上記は
exec ('print(1);'*5)
と書くことが出来ます。
配列や文字列を逆順にする
配列に対しては通常reversed()
関数を使いますが、スライスのスキップ機能を使うことでもっと短く表現出来ます。
[1,2,3,4][::-1] # => [4,3,2,1]
'abcd'[::-1] # => 'dbca'
要素の追加は append や extendではなく += を使う
A.append(B)
は A+=[B]
と書けます。
A.extend(B)
は A+=B
と書けます。
一行に複数の文を書く(複合文)
使用例
while foo(a):print(a);a*=2
セミコロンで区切ることで複合文を使えます。しかし、 複合文の中にif文、for文、while文等が含まれない限り1行で書くことが可能です。参考if
やfor
while
を一行で済ます目的で長々と書くことは出来ないので注意が必要です。
複数の変数の初期化
a,b,c=0,0,0
より ``a=b=c=0の方が短いです。 また文字限定ならば
a,b,c='1','2','3'` より `a,b,c='123'` の方が短く書けます。
関数呼び出し時のリスト内包表記の角括弧 [] を省略する
CheckIOで指摘されて気がついたのですが、関数に内包表記のリストを渡す場合に引数が1つの時のみ角括弧を省略することが出来ます。sum
any
all
といった関数に対してよく使います。
例えば0から9の合計を求めるコード
sum([x for x in range(10)])
は
sum(x for x in range(10))
となり2文字の節約となります。
このように書くとつまらない最適化みたいですがコードを短くしていく過程で最終的にこのような形になることが多いのでよく使います。最後のちょっとしたご褒美って感じでしょうか。
str()を短く書く (Python2限定)
str(n)
は `n`
と同じですがこの表現はPython3.xでは出来ません。