DT日記

家を離れた自宅警備員の日記

難読コードを読んでみよう(Python初心者向け解説)

こちらに種も仕掛けもないPythonのコードがありまして:
これを実行しますと真ん中の地球儀が45度回りまして
8回実行すると元に戻るわけです。

Qlobe.py - 西尾泰和のはてなダイアリー

また nishio さんが頭のをかしいことを… と思ったけど、よく読んでみるとやってることはすごく単純で、コードを読む練習としてすごく入門者向きなんじゃないかと思ひました。

ってわけで、初心者のひとでもそのおもしろさをわかっていただきたいと思って解説記事を書いてみました(核心ラスボスについては書きません)。

自分だけの力で読んでみたいひとは、解けるまで決して続きを読んではいけません。

ヒントが欲しいひとは 西尾泰和のはてなダイアリー - Pythonとワンライナーについて を読んでおくと、ためになります。


……………
…………
………
……

:


以下、コードは適宜読みやすいように編輯して掲載します。コードは python もしくは コマンドで一行づつ実行してみませう。

ipython でも動きますが、ふつうの python の方が良い気がします。

LinuxMac の方は端末/ターミナルを開いて python と打ち込むだけです。

C = "The Qlobe (c)Yusuke Endoh, 2010. Ported to Python by NISHIO Hirokazu, 2012."

この一行は誰にでもわかると思ひます。このコードからターミナルに貼り付けて、 Enter を押してやればその場で実行されます。

何も表示はされないと思ふので、 C とだけ打って Enter してやれば、変数 C の中身がわかります。 print C とやっても良いです(意味は微妙に違ひます)。

v = 0000

これも、ただ整数を代入してるだけですね。何の意味があるのかはおいおいわかるでせう。

s = """import zlib,struct                  ,math,sys,re;d=re.sub('\s+|".*'
'"',"", '''^Lcf<LK8,_@7gj*       "%.#%  :::##"       LJ=c5nM)Tp1g0%Xv.,S[<>YoP4
ZojjV)O>qIH1/n[|2yE[>:i     "##%      ::##########"     eC97N-A&Kj_K_><wS5rtWk@
*a+Y5yH?b[F^e7C/56j|p    "##:         ###############"    mRe+:)BO98(Zh)'Iof*nm
.,$C5Nyt=PPu01Avw^<    "#            #.   .####:#######"    IiQ=5$'D-y?g6`YT+qL
w9k^ch|K'),tc6ygIL   "              ##### # :############"   8xI#LNz3v}T=4WlL27
FZ0ij)7TQCI)P7u}R   "              #######################"   T5-iJbbG5P-DHB<.R
,YvZ_rnv6ky-G+4U   "              ############:####  %#####"   '$*are@b4U351Q-u
g500x8RR%`Om7VD   "              .#############:##%   .##  ."   p4M5PFixrPvl&<p
[]1IJEGgDt8Lm#;  " %%            .################.     #.   "  bc4zS^y]0`_Pstf
UxOC(q/,}.YOIFj  ":####:          :##############%       :   "  (k&q_VzcaAi?]^l
CVYp''');v=([v,  "%######.              #########            "  ][0]-int(((sys)
.argv[1:]+[45,[  "##########.           #######%             "  ]])[0]))%360;s;
s=('v=%04o;'%v+  "###########           :######.             "  *0+'s=""'+'"'+s
+'""'+'";exe'+[  ":#########:           .######: .           "  ][0]*0+'c(s)');
d=[([ord(c)] or   " :#######%           :###### #:          "   )[0]for c in d]
f=lambda x,y:x*[   "  ######%           .####% ::          "   ,90][1]+(y-2)%91
t="%x"%reduce(f,[   "  ####%             %###             "   ,d][1],0);e=(zlib
.decompress);e=e([   " .###:             .#%             "   ,t.decode('hex')][
1]);j="".join;e=j((    " %##                           "    *0+"{0:08b}".format
(ord(b)))for b in e);    " #.                        "    ;s=list(s);z=360;p=(s
.__setitem__);q,_,r=[90,    " .                   "     ,range];[(lambda w=int(
(math.sqrt(1-((y*2.0-21)/(       "#  :#######"       and 22))**2)*23)):[(lambda
t=q*x/w+v+q:(lambda u=(e[y*z:(                  y+1)*z]*2)[t:t+q/w]:p(y*80+120-
w+x," .:%#"[4*u.count("0")/len(u)]))()) ()for x in r(w*2-1)])() for y in r(22)]
print(("%sC=%r%s"%(chr(10),C,chr(10))))+j(s)+chr(10) #Happy Hacking!"""

このひとかたまりは、長い長ーい文字列を s といふ変数に代入してゐます。 """ といふのが気になると思ふが、これは Python 独特の longstring と呼ばれる文字列リテラルですね。

要するに改行を含むことができるのだと覚えておけば良いです。

RubyPerl, シェルスクリプトなどの言語での ヒアドキュメント といふ形式と同じような感じのものだと思っておいてください。

exec(s)

exec に文字列を渡すと、 Python のプログラムとして実行されます。 RubyPerlJavaScript を書かれる方は eval をご存じでせう。 Python にもこれらの言語とよく似た名前の eval 函数 があります。 Pythonexec(文) と eval(函数) には歴然とした違ひがあるのですが、今回は説明を割愛します。 Pythoneval には言語仕様に基く強い制約があるのです。 それは Python を知る上で必ず避けて通れない差です*1

話が長くなりましたが、文字列として書かれたプログラムはこの行で実行されます。これを実行すると、次の地球儀のコードが画面に表示されると思ひます。実行する上では一瞬で過ぎ去ってしまひますが、コードを読解する上ではここからが本番だと言っても良いでせう。

      • -

さて、

s = """import zlib,struct                  ,math,sys,re;d=re.sub('\s+|".*'
'"',"", '''^Lcf<LK8,_@7gj*       "%.#%  :::##"       LJ=c5nM)Tp1g0%Xv.,S[<>YoP4
ZojjV)O>qIH1/n[|2yE[>:i     "##%      ::##########"     eC97N-A&Kj_K_><wS5rtWk@
*a+Y5yH?b[F^e7C/56j|p    "##:         ###############"    mRe+:)BO98(Zh)'Iof*nm
.,$C5Nyt=PPu01Avw^<    "#            #.   .####:#######"    IiQ=5$'D-y?g6`YT+qL
w9k^ch|K'),tc6ygIL   "              ##### # :############"   8xI#LNz3v}T=4WlL27
FZ0ij)7TQCI)P7u}R   "              #######################"   T5-iJbbG5P-DHB<.R
,YvZ_rnv6ky-G+4U   "              ############:####  %#####"   '$*are@b4U351Q-u
g500x8RR%`Om7VD   "              .#############:##%   .##  ."   p4M5PFixrPvl&<p
[]1IJEGgDt8Lm#;  " %%            .################.     #.   "  bc4zS^y]0`_Pstf
UxOC(q/,}.YOIFj  ":####:          :##############%       :   "  (k&q_VzcaAi?]^l
CVYp''');v=([v,  "%######.              #########            "  ][0]-int(((sys)
.argv[1:]+[45,[  "##########.           #######%             "  ]])[0]))%360;s;
s=('v=%04o;'%v+  "###########           :######.             "  *0+'s=""'+'"'+s
+'""'+'";exe'+[  ":#########:           .######: .           "  ][0]*0+'c(s)');
d=[([ord(c)] or   " :#######%           :###### #:          "   )[0]for c in d]
f=lambda x,y:x*[   "  ######%           .####% ::          "   ,90][1]+(y-2)%91
t="%x"%reduce(f,[   "  ####%             %###             "   ,d][1],0);e=(zlib
.decompress);e=e([   " .###:             .#%             "   ,t.decode('hex')][
1]);j="".join;e=j((    " %##                           "    *0+"{0:08b}".format
(ord(b)))for b in e);    " #.                        "    ;s=list(s);z=360;p=(s
.__setitem__);q,_,r=[90,    " .                   "     ,range];[(lambda w=int(
(math.sqrt(1-((y*2.0-21)/(       "#  :#######"       and 22))**2)*23)):[(lambda
t=q*x/w+v+q:(lambda u=(e[y*z:(                  y+1)*z]*2)[t:t+q/w]:p(y*80+120-
w+x," .:%#"[4*u.count("0")/len(u)]))()) ()for x in r(w*2-1)])() for y in r(22)]
print(("%sC=%r%s"%(chr(10),C,chr(10))))+j(s)+chr(10) #Happy Hacking!"""

をもう一回だけ実行しておいてください。 これは非常に重要です

ではここから、文字列 s の中身をさらに分解して読んでいきます。

import zlib, struct, math, sys, re

必要なモジュール(ライブラリ)をここでインポートします。これらは全て Python 2.7 標準ライブラリ に載ってますので、余裕があれば読んでおいてください。

まあそれは後でも良いので、まずは先に進みます。

d = re.sub(
        '\s+|".*"',
        "",
        '''^Lcf<LK8,_@7gj*       "%.#%  :::##"       LJ=c5nM)Tp1g0%Xv.,S[<>YoP4
ZojjV)O>qIH1/n[|2yE[>:i     "##%      ::##########"     eC97N-A&Kj_K_><wS5rtWk@
*a+Y5yH?b[F^e7C/56j|p    "##:         ###############"    mRe+:)BO98(Zh)'Iof*nm
.,$C5Nyt=PPu01Avw^<    "#            #.   .####:#######"    IiQ=5$'D-y?g6`YT+qL
w9k^ch|K'),tc6ygIL   "              ##### # :############"   8xI#LNz3v}T=4WlL27
FZ0ij)7TQCI)P7u}R   "              #######################"   T5-iJbbG5P-DHB<.R
,YvZ_rnv6ky-G+4U   "              ############:####  %#####"   '$*are@b4U351Q-u
g500x8RR%`Om7VD   "              .#############:##%   .##  ."   p4M5PFixrPvl&<p
[]1IJEGgDt8Lm#;  " %%            .################.     #.   "  bc4zS^y]0`_Pstf
UxOC(q/,}.YOIFj  ":####:          :##############%       :   "  (k&q_VzcaAi?]^l
CVYp'''
    )

re.sub正規表現で文字列を置換して返す函数です。 '\s+|".*" は検索する正規表現です。 "" はマッチした箇所を空文字列に、つまり消し去ります。

ちょっと次のコードを動かしてどのような動きをしてるのか確かめてみませう。

_p = r'\s+|".*"'
_s = '''^Lcf<LK8,_@7gj*       "%.#%  :::##"       LJ=c5nM)Tp1g0%Xv.,S[<>YoP4
ZojjV)O>qIH1/n[|2yE[>:i     "##%      ::##########"     eC97N-A&Kj_K_><wS5rtWk@
*a+Y5yH?b[F^e7C/56j|p    "##:         ###############"    mRe+:)BO98(Zh)'Iof*nm
.,$C5Nyt=PPu01Avw^<    "#            #.   .####:#######"    IiQ=5$'D-y?g6`YT+qL
w9k^ch|K'),tc6ygIL   "              ##### # :############"   8xI#LNz3v}T=4WlL27
FZ0ij)7TQCI)P7u}R   "              #######################"   T5-iJbbG5P-DHB<.R
,YvZ_rnv6ky-G+4U   "              ############:####  %#####"   '$*are@b4U351Q-u
g500x8RR%`Om7VD   "              .#############:##%   .##  ."   p4M5PFixrPvl&<p
[]1IJEGgDt8Lm#;  " %%            .################.     #.   "  bc4zS^y]0`_Pstf
UxOC(q/,}.YOIFj  ":####:          :##############%       :   "  (k&q_VzcaAi?]^l
CVYp'''

re.findall(_p, _s)

re.findall は、マッチした部分を全て取り出して列挙する函数です。つまり、この函数で取り出された部分が、空文字列に置換されて消えます。

print d
^Lcf<LK8,_@7gj*LJ=c5nM)Tp1g0%Xv.,S[<>YoP4ZojjV)O>qIH1/n[|2yE[>:ieC97N-A&Kj_K_><wS5rtWk@*a+Y5yH?b[F^e7C/56j|pmRe+:)BO98(Zh)'Iof*nm.,$C5Nyt=PPu01Avw^<IiQ=5$'D-y?g6`YT+qLw9k^ch|K'),tc6ygIL8xI#LNz3v}T=4WlL27FZ0ij)7TQCI)P7u}RT5-iJbbG5P-DHB<.R,YvZ_rnv6ky-G+4U'$*are@b4U351Q-ug500x8RR%`Om7VDp4M5PFixrPvl&<p[]1IJEGgDt8Lm#;bc4zS^y]0`_PstfUxOC(q/,}.YOIFj(k&q_VzcaAi?]^lCVYp


さて、無駄な部分を取り去った後のコードですが、まったく意味不明な文字の並びなので、いまは考へても無駄っぽいです。次に進みませう。

print v
v = ([v, "%######.              #########            "][0] -
     int(( (sys).argv[1:] + [45, ["##########.           #######%             "]])[0])) % 360
print v

これを実行すると最初に「315」次に「270」が表示されます。さて、このコードは何の意味があるのでせう……?

各自考へていただくとして、さらに詳しく見ていきます。

_l = [v, "%######.              #########            "]
print _l[0]
print _l[1]

非常にわざとらしいコードです。 "%######. ######### " には何の意味もなく、ただ捨てられるだけです。

_a = sys.argv[1:]
_b = [45, ["##########.           #######%             "]]
_c = (_a + _b)[0]
_d = int(c)

一行づつ実行してみて、何をやってるのか調べてみてください。非常に無駄なことをしてるとわかるはずです。

s;

これは何も意味がない一文ですね。元のコードを見返してほしいのですが、これで2文字なのでうまく文字数の調整に差し込まれてます。

_s = 'v=%04o;' % v + "          .#%#######%#.         : ###### . " * 0 + 's=""' + 
    '"' +s +'""' + '";exe' + ["          .::#######::.         . ###### : "][0] * 0 + 'c(s)'
print _s

元のコードでは s に代入してますが、さしあたって _s に代入しておきます。なぜそんなことをするのかといふと、コードをよく読んでほしいのですが、このコードを実行する前と後で変数 s の内容が変化してしまうからです。 print sprint _s を実行して、見比べてみてください。

_v = 'v=%04o;' % v
print _v
_1 = "          .#%#######%#.         : ###### . " * 0
_r = 's=""' + '"' +s +'""' + '";exe'
_2 = ["          .::#######::.         . ###### : "][0] * 0
_t = 'c(s)'
_u = _v + _1 + _r + _2 + _t
print _u
print _s == _u

Python% 演算子には、標準的に用ゐられるものではおよそ二つの意味があります。 数値演算の剰余文字列フォーマット操作 です。文字列フォーマット操作とは、 C言語でいふところの sprintf とよく似てます。書き方のルールもだいたい同じです。

C言語って良く知らないんですけど、書くならたぶんこんな感じになりますかね。 (ideone で動かしてみる)

#include<stdio.h>

int main(void){
    int v = 315;
    char s[8] = "";
    sprintf(s, "%04o", v);
    puts(s);

    return 0;
}

printf 書式は PerlRuby などでも使へるので、覚えておくと非常に有用な気がしますね。

なほ、この % 演算子の存在を教へておいてから言ふのもなんなのですが、「モダンな」 Python では % ではなく str.format を使ふのナウいぜ、ってことになってるので注意してください。

次に文字列・リストのかけ算です。

print 'a' * 10
print [1] * 5

Python では文字列やリストといった型のオブジェクト (シーケンス型 と総称されます) は、かけ算することでその要素を並べることができます。さて、 0 をかけるとどんな結果になるのでせうか……?

次に進みませう。その前に、 _ss を見比べ終ったら、 忘れずに次のコードを実行しておいてください

s = _s

もとが s にそのまま代入するコードだったので、ここで帳尻を合はせます。

_d = [ ([ord(c)] or " :#######%           :###### #:          ")[0] for c in d]

これは リスト内包 と呼ばれる記法です。これを知らずに Python のリストを扱ふのはモグリのやることです><

例を挙げませう。 Python で「0〜100までの整数のリスト」を作るには range(0, 101) と書きます。なら、「0〜100までの浮動小数点数のリスト」あるいは「0〜100までの文字列のリスト」を作りたくなったときはどうしますか。

for 文を活用して次のやうに書くこともできます。

o = range(0, 101)   # 0〜100の整数のリスト
l = []              # 空のリスト。ここに要素を追加
for n in o:         # o の内容をひとつづつ走査
    f = float(n)    # 要素と int から float に変換
    l += [f]        # リストに一つづつ追加する

このコードは5行もありますね(注釈を入れる都合上、わざと行を分けてる部分があるので、それをまとめても3行になります)。

ところがリスト内包を使ふと、次のように書き直すことができます。

l = [ float(n) for n in range(0, 101) ]

はい、すごくシンプルに書けました。ほとんど無駄がありません。知らなかったならぜひ覚えていってくださいね。

ord 函数 は文字のコードポイントを返す函数です。文字は数字に、そして逆に数字は文字に変換できることを覚えておいてください。数字から文字に変換するのは chr 函数 もしくは unichr 函数 です。

and, or などを ブール演算子 と呼びます。 (x == y) or (x == z) のように使ふのが一般的ですが、 Python などの言語*2ではちょっと違った書きかたができます。

print "" or "b"
print "a" or "b"
print "a" and "b"
print not "a" or "b" and "c"
print "a" and "b" and not "c"
print "a" and "b" or not "c"

一般的なブール代数の演算では False or True ならば TrueTrue and False ならば False のように評価されます。 Python でも False が返ってくることは同じですが、 True ではなく、最後に評価されたオブジェクト が返ってきます。

print (["foobar"] or "hogehoge")[0]

こんなふうに書くと、表示されるのは……?

f = lambda x, y: x * ["  ######%           .####% ::          ", 90][1] + (y - 2) % 91

これは ラムダ式 といふ記法で、次のような函数定義とおよそ同じ意味です。

def f2 (x, y):
    return x * ["  ######%           .####% ::          ", 90][1] + (y - 2) % 91

Pythonラムダ式は他の言語と比べて制約が大きく、慣れないと非常に使ひにくいものになってます((lambda なんて使ってないで、大人しく def函数定義してろや、といふ言語設計者のメッセージが見え隠れしますね!))。これに関しては「もし使へる場面があるなら使っても良いんじゃね?」程度のものですが、 Python は制約が大きいので、このプログラムのように縛りプレイではたいへん便利なものになってゐます。

この函数もわざわざたいへんめんどくさい書き方になってるといふことは、ここまで読んでくれたみなさんなら既におわかりのことだと思ひます。

t = "%x" % reduce(f,["  ####%             %###             ", d][1], 0)

reduce 函数 は、いはゆる「畳み込み」函数です。……とだけ言はれても困るので、リストの値の合計を求めることを考へてみます。

l = range(1, 101)
print sum(l)

Python には組み込み函数 sum があるので、正しい計算結果が「5050」であることはここで確認できます。

次に reduce を使って書いてみます。

l = range(1, 101)

def f (acc, item):
    return acc + item

print reduce(f, l, 0)

acc には最後に求めたい値の途中の計算結果が、 item にはリストから取り出されたアイテムが順番に代入されます。この l には要素が100個ありますので、 f は100回呼び出され、 0 + 1 (=> 1), 1 + 2 (=> 3), 3 + 3 (=> 6), 6 + 4 (=> 10), 10 + 5 (=> 15), 15 + 6 (=> 21), ... , 4950 + 10 (=> 5050) と、延々と計算してくれます。

では、reduce を使ってリストの長さを求めるにはどうするのが良いのでせう……? ちょっと考へてみてください。

……脇道に逸れすぎましたね。前述のコードの結果、変数 t の中身は、さらにわけのわからない文字列になります。

78da8553d16d8330103d4425a4aa0a1da0121d21032079a54c80bd4147e90a7c54ea1a541d2054f97114cb2f07061b63477d3f9cf1f3dd7b7736c8e345d98a3f230079a2411414416146e356259175eb9676e8add0c0
86fdb472d7c31b9c2c3cc4b60e50984b7b203aaed4a3fec006f5f4cf17e2b81d9f435efd89082c41aea5aabd088d1da8335e851355d020895e335c3635c4643ab0df6a04f5b8246c7ccb2f17944b753edd189c15ce297
97219756e2a7f45032132e42a746ff6b8841d65b84eabf6e4c59718fb3f51d82c59f929f97d6e53dd6734bb6bc4178923730b3b5d92194186988718b2bceb9cc3a538671e1a93ba8dc9d6ebd7518e748c25791fb5da0e
f78d1da49af10042b9ae04c887dc19bff1f207ff235cdcd1bf9a146c47de01e8239431

この意味もやっぱりわからないので、次に進みます。

e = zlib.decompress

変数 ezlib.decompress といふ函数を代入してるだけ… と言ってしまふとそれで終了なんですが、いったいどういふことでせう。

def myprint(s):
    print s

こんな函数を定義してみてください。

これは myprint("Hello, World!") と書いてやることで print 文と同じように使ふことができます。

ところが、こんなふうに扱ふこともできるのです。

p = myprint

p("Bye Bye, Dragon World!")

*3

みなさまが驚くかはわかりませんが、 Python では函数名と変数名は同じように扱ふことができる、まったく同列のものだったのです! これは Python 標準の組み込み函数に対してももちろん同様で、 i = int; i("100") と書くことすらできます。 JavaScript に詳しいみなさんは別に驚くことではないのかもしれません。しかし Ruby が大好きな僕たちには衝撃的な事実です!((Ruby のメソッドを変数の世界に持ち込むことは、可能です。しかしそれは、 Method オブジェクトといふ別の姿に成り果てたものです。))

つまり、 zlib.decompress といふ長ったらしい名前の函数e 一文字で使ふことができるようになりました。飽くまで強調しておきますが、これはふつうのプログラムで使ふとわかりにくくなるだけなので、濫用してはいけません。

e = e([" .###:             .#%             ", t.decode('hex')][1])

ああ、これはひどい! さっき zlib.decompress を代入したばかりの e にまったく関係ないものを最代入してます。いかにも意地悪な書き方ですね。これは楽しい。後の方を読まなければわかりませんが、この e = は、たとへば k = とかでも良いはずです。意味のわからないコードにしてやったぞといふ意図が見え隠れします><

このコードの結果、 e の中身は大量の \xnn の羅列と化します。

j = "".join

これもさきほどの函数の名前の付け替えです(正確にはメソッドですが、 Python函数とメソッドはほとんど同じものです)。

l = range(1, 6)
print "".join(l)
print ",".join(l)

Pythonjoin メソッドはこのように使用します。 Rubyjoin にお馴染の方は気持ち悪く見えるかもしれませんね。僕は慣れました。

e = j((" %##                           " * 0 + "{0:08b}".format(ord(b))) for b in e)

ここでも内包表記が使用されてますね。先に紹介した str.format も出現してます。いよいよ終盤に近づいて大詰めって感じがしてきました。

このコードの結果、 e の中身は大量の 01 の羅列と化します。

" #.                        ";

ここまできてなんですが、これはただの文字列です。代入もされず、文や函数にも渡されない文字列はプログラムの他の箇所に何の影響をも及ぼしえません。ぼーっとそこにつっ立ってるだけです。ほんとにいまさらですね。

_s = list(s)

念のため、 print s で中身が何だったのか確認しておいてください。気が済んだら s = _s して次に進んでください。

list("abc")['a', 'b', 'c'] になります。

z = 360
p = s.__setitem__
q, _, r = [90, " .                   ", range];

ラスボス直前のインターミッションです。

最後のは多重代入で、q, _, r のみっつの変数に一気に代入することができます。今回のような縛りのあるプログラムでは、文字数の調整(特に短縮)に便利です。また、このような場合の _ は、使ひ捨てにして再利用しない変数に宛てることが多い名前です。

object.__setitem__ メソッド は、コレクションへの代入を行ふメソッドです。

l = [1, 2, 3]
print l[0] # -> 1

l[0] = 10
l.__setitem__(1, 20)

print l # -> [10, 20, 3]
print l.getitem(0) # -> 10

こんな感じになります。

[
    (lambda w = int((math.sqrt(1 - (( y * 2.0 - 21) / ("#  :#######" and 22) ) ** 2) * 23)):
        [(lambda t = q * x / w + v + q:
            (lambda u = (e[y * z : (y + 1) * z] * 2)[ t : t + q / w]:
                p(y * 80 + 120 - w + x, " .:%#"[4 * u.count("0") / len(u)])
            )()
        )() for x in r(w * 2 - 1)]
    )()
for y in r(22)]

あのですねあのですね、このコードを Python って呼んで良いんですかレベルのひどさだと思ひませんか……? 一見してこれは新手の Lisp かよと言ひたくなりますね><

それにしても邪悪…! 先ほど代入もしない函数にも文にも渡さない文字列は何も意味がないと言ひましたが、リストも同じです。

[n for n in range(1,10)]

これは何の意味もありません。表示も代入もしないからです(python コマンドの対話環境では表示されますが)。しかし、この長いリスト内包は何をしてるのでせうか……! 不純は不潔!

このコードについて僕がナビゲーションできるのはここまでです。自分でがんばって読み解いてください。

さて、ラスボスは倒した(ことになりました)ので、あとは print で表示してやるだけです。

_s = "%sC=%r%s" % (chr(10), C, chr(10))
print _s + j(s) + chr(10)
#Happy Hacking!

Happy Hacking!

地球儀は表示されましたか?

……いやあ、 Qlobe.py は強敵でしたね。

感想

  • これ Python のすごく良い教材だと思ひます
    • リスト内包とか reduce の紹介ができますし
      • 綺麗な使ひ方と邪悪な使ひ方の両方があったのは良かった(棒読み)
    • 意地悪なコードを読む力は身につく(ついた)気がします><
  • 先の見えないまま解説を書きながらコードを読み進めたけど、「僕にはわからん!お手挙げです!」にならなくて良かった良かった
    • さすが腐っても Python なので、文の区切りはすごくわかりやすかった
    • Ruby原作 をこれを同じ調子で解説できる気がまったくしない…

参考資料

//python.jp/doc/release/">Python 2.7ja1 ドキュメント:情報いっぱいだから、ここ読まないのは損だよ!
//www.nishiohirokazu.org/blog/2006/08/python_12.html">西尾泰和のブログ - Pythonワンライナーを作成する際のノウハウ集:2年ちょっと前に読んで全然理解できなかったけど Python おもしろいなって思ひました
//www.nishiohirokazu.org/blog/2007/08/post_323.html">西尾泰和のブログ - ワンライナーはダークサイド。改行をいれるべし:正方形でコードを書くなんて、このひと頭がをかしいなって思ひました

*1:そしてそれは、 Ruby ではとても曖昧な差です

*2:細かい仕様は異なりますが、 RubyPerl でもいけます

*3:この元ねたが国民的漫画「ドラゴンボール」最終回であることは、ドラゴンボール大好き国民のみなさまには説明するまでもないことです!