ワンライナー、とは
pythonでワンライナーを、つまり一行だけからなるプログラムを書くだけなら、かなり簡単です。
exec("for i in range(10):¥n¥tprint(i)")
このコードを読んだあなたは思うでしょう。「execは反則だ」と。しかし、本当にそうでしょうか?
pythonにはワンライナーを容易にする様々な仕組みがあります。
文法 | 使用方法 |
---|---|
exec , eval
|
文字列をコードとして実行 |
; |
改行の代わり |
open |
ファイルをストレージとして利用 |
__import__ |
モジュールのインポート |
globals() |
名前空間を辞書として操作 |
lambda 式 |
関数の定義や遅延評価 |
type |
クラスの定義 |
三項演算子(条件演算子) | 条件によって異なる値 |
ワンライン制御文 | 一回だけなら制御文が使える |
リスト内包表記 | 独自の名前空間内でfor文を回す |
or やand
|
短絡評価 |
iter の第二引数 |
for文で無限ループ |
print などの返り値がNone |
短絡評価と組み合わせて関数実行 |
破壊的メソッド | 値を書き換える |
ビルトイン関数や変数の上書き | 初期化が不要な変数 |
quit() |
breakの代わり |
generator.throw |
raiseの代わり |
format文字列 | evalと似た使い方 |
:= |
python3.8より使える代入式 |
ここまで用意されると分かると思いますが、pythonはワンライナーのために設計された言語なのです。
しかしここまで親切だと、今度は「どこまで使っていいのか」が分かりません。exec
と;
はずるい匂いがしますが、それぞれの制限もありますし難しさもあります。globals()
やlambda
はpythonワンライナー記事でよく見かけますが、関数型言語の知識と組み合わせれば猛威を振るうことは容易に想像できます。
ということで、読者の皆さんに挑戦状です。
「使用文法が極小のチューリング完全なワンライナーを書いて下さい」
つまり、
- 可読性のために改行してもいいですが、改行しなくても動くようにして下さい。
- チューリング完全である事を示して下さい。
- エラーをraiseする必要はありません。
- 上に書いた仕組みは「使わない方がいい文法」とします。
- その他の制限を自分に課しても構いません。
- 使用する文法は極小にして下さい。
- 極小である、とは、使用した文法の部分集合ではチューリング完全ワンライナーが書けない、という意味です。
- lambdaを一回しか使わない、などの縛りも歓迎です。
- 極小であることは示さなくても構いません。
- 極小でない他人のコードを改良しても構いません。ただし、簡単な変換をして勝ち誇るのはやめて下さい。
そのほか、ワンライナーで面白いことをした報告とかもお待ちしてます。
コード例
リスト内包表記・返り値がNone・短絡評価・破壊的メソッド
bfが実装できるのでチューリング完全です。
ちなみにスライスを使ってるのは純粋なリスト内包表記じゃない、と言われた記憶がありますが、リスト内包表記の中でenumerateとか使えば余裕で変換できるので、そこを改良して勝ち誇らないで下さい。
以下はbfのインタープリター
[None for result in[[i[-1]for arr in[[[None,[int(i)%0xff for i in input().split()],input()+' ',{'0':0},'0',0,'',]]]for i in arr if(i[5]<len(i[2]))and not arr.append([[[i[3].update({i[4]:0})for _ in[None]if i[4]not in i[3]],[[i[1][1:]for _ in[None]if i[2][i[5]]is',']+[i[1]]][0][0],i[2],([[dict([(other,i[3][other])for other in i[3]if other is not i[4]]+[(i[4],(i[3][i[4]]+1)%0xff)])for _ in[None]if i[2][i[5]]is'+']+[dict([(other,i[3][other])for other in i[3]if other is not i[4]]+[(i[4],(i[3][i[4]]- 1)% 256)])for _ in[None]if i[2][i[5]]is'-']+[dict([(other,i[3][other])for other in i[3]if other is not i[4]]+[(i[4],i[1][0])])for _ in[None]if i[2][i[5]]is',']+[i[3]]][0][0]),([[str(int(i[4])+ 1)for _ in[None]if i[2][i[5]]is'>']+[str(int(i[4])- 1)for _ in[None]if i[2][i[5]]is'<']+[i[4]]][0][0]),([[i[5]+([length for length in range(2,len(i[2])+1)if (i[2][i[5]:][:length].count('[')is i[2][i[5]:][:length].count(']'))][0])for _ in[None]if i[2][i[5]]is'['and not i[3][i[4]]]+[i[5]+(-[length for length in range(2,len(i[2])+1)if(i[2][:i[5]+1][::-1][:length].count('[')is i[2][:i[5]+1][::-1][:length].count(']'))][0]+1)for _ in[None]if i[2][i[5]]is']'and i[3][i[4]]]+[i[5]+1]][0][0]),[[i[6]+chr(i[3][i[4]])for _ in[None]if i[2][i[5]]is'.']+[i[6]]][0][0],]][0])]]if print(result[-1])]
多分、短絡評価も消せるんですけど、試す体力ないです。
詳細は別記事を読んで下さい。
ワンラインfor文・iter・quit()・短絡評価・返り値がNone
gotoが実装できてるので、bfと一対一対応し、チューリング完全です。(多分
以下はFizzbuzzコード。
for i,_ in enumerate(iter(list,None)):
ln, ns = i == 0 and (1,{}) or (
(ln == 1 and (2,{**ns,'i':1})) or
(ln == 2 and (ns['i']<=20 and (3,ns) or (8,ns))) or
(ln == 3 and (ns['i']%15==0 and (3.5,ns) or (4,ns))) or
(ln == 3.5 and not print('fizzbuzz') and (7,ns)) or
(ln == 4 and (ns['i']%5==0 and (4.5,ns) or (5,ns))) or
(ln == 4.5 and not print('buzz') and (7,ns)) or
(ln == 5 and (ns['i']%3==0 and (5.5,ns) or (6,ns))) or
(ln == 5.5 and not print('fizz') and (7,ns)) or
(ln == 6 and not print(ns['i']) and (7,ns)) or
(ln == 7 and (2,{**ns,'i':ns['i']+1})) or
(ln == 8 and quit())
)
代入文を使ってるワンライナーは珍しいと思います。
基本的なアイディアは
- ns:名前空間、ln:行番号
- 再代入で更新
- 短絡評価を使うことで、lnの値によって実行する式を変えてます
- iterで無限ループをして、quit()で抜けてます
- iの値で初回ループ(ln, nsが未定義)か、2回目以降(既定義のln,nsを使って計算)かを調べています
lambdaの再帰を使ったワンライナーやwhileを使ったワンライナーも参考にして下さい。
その他
- 制御文というのはつまり「書いてある式を無視する・繰り返す」というものです。リスト内包表記か短絡評価かlambdaを使った遅延評価以外で実装する方法はないと思います。そういうのがあって、教えてくれたらガチ尊敬します。