LoginSignup
16
22

More than 3 years have passed since last update.

【挑戦状】あなたはpythonワンライナーが書けますか?

Last updated at Posted at 2018-12-24

ワンライナー、とは

pythonでワンライナーを、つまり一行だけからなるプログラムを書くだけなら、かなり簡単です。

exec("for i in range(10):¥n¥tprint(i)")

このコードを読んだあなたは思うでしょう。「execは反則だ」と。しかし、本当にそうでしょうか?

pythonにはワンライナーを容易にする様々な仕組みがあります。

文法 使用方法
exec, eval 文字列をコードとして実行
; 改行の代わり
open ファイルをストレージとして利用
__import__ モジュールのインポート
globals() 名前空間を辞書として操作
lambda 関数の定義や遅延評価
type クラスの定義
三項演算子(条件演算子) 条件によって異なる値
ワンライン制御文 一回だけなら制御文が使える
リスト内包表記 独自の名前空間内でfor文を回す
orand 短絡評価
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を使った遅延評価以外で実装する方法はないと思います。そういうのがあって、教えてくれたらガチ尊敬します。

スペシャルサンクス

  • @koji-kojiro : generator.throw()を教えて下さいました
  • @cocuh : ワンライナーを調べる段階で色々お世話になりました 記事
  • @gyu-don : lambdaの振る舞いの勉強になりました 記事
  • @kzm4269 : iter()を教えて下さいました
  • @t-sin : リスト内包表記関連で色々と発想をもらいました
16
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
22