サルでもわかるPython顔文字プログラミング

takesako
2010-12-09

こんにちは。id:TAKESAKOです。

サルでもわかるシリーズ第一弾として、Python顔文字プログラミングの基礎を解説してみましょう。

1. Python顔文字プログラミング

以下のプログラムをPythonインタプリタ上で動かすと、'saru' という文字列が表示されます。

`(''<>'')`[-~-~(''=='')]+`(''<>'')`[''=='']+`(''=='')`[''=='':-~-~(''=='')]
'saru'

実際に Python 2.7.1 のインタプリタで実行してみると確かにそうなります。

Python 2.7.1 (r271:86832, Nov 27 2010, 17:19:03) [MSC v.1500 32 bit] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> `(''<>'')`[-~-~(''=='')]+`(''<>'')`[''=='']+`(''=='')`[''=='':-~-~(''=='')]
'saru'

どうして 'saru' という文字列が生成されたのでしょうか。

2. 真偽値の作成

まず、Pythonで文字列の比較を行うと、真偽値 True、False が返ります。

>>> (''=='')
True
>>> (''<>'')
False

記号だけで真偽値を簡単に作成することができました。

3. 真偽値から数値を作る

次に真偽値から数値に変換することを考えてみます。真偽値に対して数値演算を施すとint(True)は1に、int(False)は0として評価されます。

>>> int(True)
1
>>> True+0
1
>>> True-0
1
>>> True/1
1
>>> True+0
1
>>> True-0
1
>>> True*1
1
>>> True/1
1
>>> int(False)
0
>>> False+0
0
>>> False-0
0
>>> False*1
0
>>> False/1
0

記号だけで数値を生成したいので、単項演算子の+や-、ビット反転の~を使ってみます。

>>> +True
1
>>> -True
-1
>>> ~True
-2
>>> +False
0
>>> -False
0
>>> ~False
-1

現在のコンピュータでは、2の補数で負の数を表しているので、たとえば8bitでのビット表現は以下の数値と対応します。

ビット表現 2の補数表現 符号無し表現
00000000 0 0
00000001 1 1
01111110 126 126
01111111 127 127
11111110 -2 254
11111111 -1 255

ビット反転~演算子は0を→1に、1を→0に変換するので、~0==-1、~1==-2、となります。

4. 記号だけで任意の整数を生成する

これらを応用すると、記号だけで任意の整数を生成することができます。

>>> ~(''=='')
-2
>>> -(''=='')
-1
>>> +(''<>'')
0
>>> +(''=='')
1
>>> -~(''=='')
2
>>> -~-~(''=='')
3
>>> -~-~-~(''=='')
4
  • ~-~-~を何回も繰り返していけば、どんな整数でも生成できそうですね!

以上、ここまで大丈夫でしょうか。

5. 真偽値から文字列を作成

Pythonではバッククォートで囲うとrepr関数を呼び出したときと同等の動作となり、その評価結果をインタプリタ表現のリテラル文字列として受け取れるという仕様があります。(Python3.0ではバッククォートの表記は廃止されました)

>>> (''=='')
True
>>> `(''=='')`
'True'
>>> repr(''=='')
'True'
>>> (''<>'')
False
>>> `(''<>'')`
'False'
>>> repr(''<>'')`
'False'

これを利用すると'True'や'False'を文字列として扱うことができるので、文字列の演算を適用できます。

>>> `(''=='')`+`(''<>'')`
'TrueFalse'
>>> `(''=='')`*3
'TrueTrueTrue'

6. 部分文字列を切りだす

Pythonの文字列は、配列の要素の形式でアクセスすることにより部分文字列を取り出すことができます。

>>> `True`[0]
'T'
>>> `True`[1]
'r'
>>> `True`[2]
'u'
>>> `True`[3]
'e'
>>> `False`[0]
'F'
>>> `False`[1]
'a'
>>> `False`[2]
'l'
>>> `False`[3]
's'
>>> `False`[4]
'e'

よくみると、`False`[3]で's'が生成され、`False`[1]で'a'が生成されていますね。これを足してみましょう。

>>> 's'+'a'
'sa'
>>> `False`[3]+`False`[1]
'sa'

前半の'sa'のできあがりです!

7. 範囲指定で部分文字列を切りだす

:の記号を使って範囲指定で複数バイトの部分文字を取り出すこともできます。

>>> `True`[0:4]
'True'
>>> `True`[1:4]
'rue'
>>> `True`[2:4]
'ue'
>>> `True`[3:4]
'e'
>>> `True`[0:2]
'Tr'

ということは、'True'の1文字目から2文字目までの文字列を切り出すと、

>>> `True`[1:3]
'ru'

となり、後半の'ru'が作成できました!

7. 記号だけで 'saru' を生成する

まとまると、前半の'sa'はFalseの文字列から作成し、

>>> (''<>'')
False
>>> `(''<>'')`
'False'
>>> `(''<>'')`[3]+
's'
>>> `(''<>'')`[1]
'a'

後半の'ru'をTrueの文字列から作成すれば、'saru'の完成です!

>>> (''=='')
True
>>> `(''=='')`
'True'
>>> `(''=='')`[1:3]
'ru'

あとは数字を全部記号に書き直せば完成です。

>>> `(''<>'')`[3]+`(''<>'')`[1]+`(''=='')`[1:3]
'saru'
>>> -~-~(''=='')
3
>>> +(''=='')
1
>>> `(''<>'')`[-~-~(''=='')]+`(''<>'')`[''=='']+`(''=='')`[''=='':-~-~(''=='')]
'saru'

ね、簡単でしょ?

8. まとめ

Pythonのインタプリタでは記号だけで'saru'という文字を生成できました。
しかし、この方法では記号だけで生成できる記号以外の文字は T r u e F a l s e 0 1 2 3 4 5 6 7 8 9 L だけになってしまいます。
任意の文字列を生成するには記号の他にcの文字を使わないといけなかったり、任意の文の実行にはexecを使用したりしないと実現できないかもしれません。

これ以上の発展は読者の方への宿題としたいと思います。

以上、サルでもわかる顔文字系プログラミングの解説でした。