cipta program terkabur paling aneh yang mencetak rentetan “Hello world!”. Saya memutuskan untuk menulis penjelasan tentang bagaimana ia berfungsi. Jadi, inilah entrinya, dalam Python 2.7:
(lambda _, __, ___, ____, _____, ______, _______, ________: getattr( __import__(True.__class__.__name__[_] + [].__class__.__name__[__]), ().__class__.__eq__.__class__.__name__[:__] + ().__iter__().__class__.__name__[_____:________] )( _, (lambda _, __, ___: _(_, __, ___))( lambda _, __, ___: chr(___ % __) + _(_, __, ___ // __) if ___ else (lambda: _).func_code.co_lnotab, _ << ________, (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __) - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______ << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) << ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) << __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______ << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) + _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ << _))) + (_____ << ______) + (_ << ___) ) ) )( *(lambda _, __, ___: _(_, __, ___))( (lambda _, __, ___: [__(___[(lambda: _).func_code.co_nlocals])] + _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else [] ), lambda _: _.func_code.co_argcount, ( lambda _: _, lambda _, __: _, lambda _, __, ___: _, lambda _, __, ___, ____: _, lambda _, __, ___, ____, _____: _, lambda _, __, ___, ____, _____, ______: _, lambda _, __, ___, ____, _____, ______, _______: _, lambda _, __, ___, ____, _____, ______, _______, ________: _ ) ) )
Tersurat rentetan tidak dibenarkan, tetapi saya menetapkan beberapa sekatan lain untuk berseronok: ia mestilah satu ungkapan (jadi tiada pernyataan cetakan) dengan penggunaan terbina yang minimum dan tiada literal integer.
Bermula
Memandangkan kami tidak boleh menggunakan cetakan, kami boleh menulis pada objek fail stdout:
import sys sys.stdout.write("Hello world!\n")
Tetapi mari kita gunakan sesuatu peringkat rendah: os.write(). Kami memerlukan deskriptor fail stdout, iaitu 1 (anda boleh menyemak dengan print sys.stdout.fileno()).
import os os.write(1, "Hello world!\n")
Kami mahukan satu ungkapan, jadi kami akan menggunakan import():
__import__("os").write(1, "Hello world!\n")
Kami juga mahu dapat mengaburkan write(), jadi kami akan masukkan getattr():
getattr(__import__("os"), "write")(1, "Hello world!\n")
Ini adalah titik permulaan. Semuanya mulai sekarang akan mengaburkan tiga rentetan dan int.
Merangkai rentetan
"os" dan "tulis" agak mudah, jadi kami akan menciptanya dengan menyertai sebahagian daripada nama pelbagai kelas terbina dalam. Terdapat pelbagai cara untuk melakukan ini, tetapi saya memilih yang berikut:
"o" from the second letter of bool: True.__class__.__name__[1] "s" from the third letter of list: [].__class__.__name__[2] "wr" from the first two letters of wrapper_descriptor, an implementation detail in CPython found as the type of some builtin classes’ methods (more on that here): ().__class__.__eq__.__class__.__name__[:2] "ite" from the sixth through eighth letters of tupleiterator, the type of object returned by calling iter() on a tuple: ().__iter__().__class__.__name__[5:8]
Kami mula membuat sedikit kemajuan!
getattr( __import__(True.__class__.__name__[1] + [].__class__.__name__[2]), ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8] )(1, "Hello world!\n")
"Hello world!n" lebih rumit. Kami akan mengekodnya sebagai integer besar, yang akan dibentuk daripada kod ASCII bagi setiap aksara didarab dengan 256 kepada kuasa indeks aksara dalam rentetan. Dalam erti kata lain, jumlah berikut:
∑n=0L−1cn(256n)
di mana L
ialah panjang rentetan dan cn ialah kod ASCII bagi n
watak ke-dalam rentetan. Untuk mencipta nombor:
>>> codes = [ord(c) for c in "Hello world!\n"] >>> num = sum(codes[i] * 256 ** i for i in xrange(len(codes))) >>> print num 802616035175250124568770929992
Sekarang kita memerlukan kod untuk menukar nombor ini kembali kepada rentetan. Kami menggunakan algoritma rekursif mudah:
>>> def convert(num): ... if num: ... return chr(num % 256) + convert(num // 256) ... else: ... return "" ... >>> convert(802616035175250124568770929992) 'Hello world!\n'
Menulis semula dalam satu baris dengan lambda:
convert = lambda num: chr(num % 256) + convert(num // 256) if num else ""
Kini kami menggunakan rekursi tanpa nama untuk mengubahnya menjadi satu ungkapan. Ini memerlukan penggabung. Mulakan dengan ini:
>>> comb = lambda f, n: f(f, n) >>> convert = lambda f, n: chr(n % 256) + f(f, n // 256) if n else "" >>> comb(convert, 802616035175250124568770929992) 'Hello world!\n'
Sekarang kita hanya menggantikan dua definisi ke dalam ungkapan, dan kita mempunyai fungsi kita:
>>> (lambda f, n: f(f, n))( ... lambda f, n: chr(n % 256) + f(f, n // 256) if n else "", ... 802616035175250124568770929992) 'Hello world!\n'
Sekarang kita boleh memasukkan ini ke dalam kod kita dari sebelumnya, menggantikan beberapa nama pembolehubah di sepanjang jalan (f → , n → _):
getattr( __import__(True.__class__.__name__[1] + [].__class__.__name__[2]), ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8] )( 1, (lambda _, __: _(_, __))( lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "", 802616035175250124568770929992 ) )
Fungsi dalaman
Kami ditinggalkan dengan "" dalam badan fungsi tukar kami (ingat: tiada literal rentetan!), dan sejumlah besar yang perlu kami sembunyikan entah bagaimana. Mari kita mulakan dengan rentetan kosong. Kita boleh membuatnya dengan cepat dengan memeriksa bahagian dalam beberapa fungsi rawak:
(lambda _, __, ___, ____, _____, ______, _______, ________: getattr( __import__(True.__class__.__name__[_] + [].__class__.__name__[__]), ().__class__.__eq__.__class__.__name__[:__] + ().__iter__().__class__.__name__[_____:________] )( _, (lambda _, __, ___: _(_, __, ___))( lambda _, __, ___: chr(___ % __) + _(_, __, ___ // __) if ___ else (lambda: _).func_code.co_lnotab, _ << ________, (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __) - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______ << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) << ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) << __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______ << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) + _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ << _))) + (_____ << ______) + (_ << ___) ) ) )( *(lambda _, __, ___: _(_, __, ___))( (lambda _, __, ___: [__(___[(lambda: _).func_code.co_nlocals])] + _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else [] ), lambda _: _.func_code.co_argcount, ( lambda _: _, lambda _, __: _, lambda _, __, ___: _, lambda _, __, ___, ____: _, lambda _, __, ___, ____, _____: _, lambda _, __, ___, ____, _____, ______: _, lambda _, __, ___, ____, _____, ______, _______: _, lambda _, __, ___, ____, _____, ______, _______, ________: _ ) ) )
Apa yang sebenarnya kami lakukan di sini ialah melihat jadual nombor baris objek kod yang terkandung dalam fungsi tersebut. Oleh kerana ia tanpa nama, tiada nombor baris, jadi rentetan itu kosong. Gantikan 0 dengan _ untuk menjadikannya lebih mengelirukan (tidak mengapa, kerana fungsi itu tidak dipanggil), dan masukkannya ke dalam. Kami juga akan memfaktorkan semula 256 menjadi hujah yang dihantar kepada convert() kami yang tidak jelas. bersama dengan nombor. Ini memerlukan penambahan hujah pada penggabung:
import sys sys.stdout.write("Hello world!\n")
Lencongan
Mari kita atasi masalah yang berbeza untuk seketika. Kami mahukan satu cara untuk mengelirukan nombor dalam kod kami, tetapi ia akan menyusahkan (dan tidak begitu menarik) untuk menciptanya semula setiap kali ia digunakan. Jika kita boleh melaksanakan, katakan, julat(1, 9) == [1, 2, 3, 4, 5, 6, 7, 8], maka kita boleh membungkus kerja semasa kita dalam fungsi yang mengambil pembolehubah yang mengandungi nombor daripada 1 hingga 8, dan gantikan kejadian literal integer dalam badan dengan pembolehubah ini:
import os os.write(1, "Hello world!\n")
Walaupun kita perlu membentuk 256 dan 802616035175250124568770929992 juga, ini boleh dibuat menggunakan operasi aritmetik pada lapan nombor "asas" ini. Pilihan 1–8 adalah sewenang-wenangnya, tetapi nampaknya merupakan jalan tengah yang baik.
Kita boleh mendapatkan bilangan hujah yang diambil oleh fungsi melalui objek kodnya:
__import__("os").write(1, "Hello world!\n")
Bina satu tuple fungsi dengan argcount antara 1 dan 8:
getattr(__import__("os"), "write")(1, "Hello world!\n")
Menggunakan algoritma rekursif, kita boleh mengubahnya menjadi output julat(1, 9):
"o" from the second letter of bool: True.__class__.__name__[1] "s" from the third letter of list: [].__class__.__name__[2] "wr" from the first two letters of wrapper_descriptor, an implementation detail in CPython found as the type of some builtin classes’ methods (more on that here): ().__class__.__eq__.__class__.__name__[:2] "ite" from the sixth through eighth letters of tupleiterator, the type of object returned by calling iter() on a tuple: ().__iter__().__class__.__name__[5:8]
Seperti sebelum ini, kami menukar ini kepada bentuk lambda:
getattr( __import__(True.__class__.__name__[1] + [].__class__.__name__[2]), ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8] )(1, "Hello world!\n")
Kemudian, ke dalam bentuk rekursif tanpa nama:
>>> codes = [ord(c) for c in "Hello world!\n"] >>> num = sum(codes[i] * 256 ** i for i in xrange(len(codes))) >>> print num 802616035175250124568770929992
Untuk keseronokan, kami akan memfaktorkan operasi argcount ke dalam argumen fungsi tambahan dan mengelirukan beberapa nama pembolehubah:
>>> def convert(num): ... if num: ... return chr(num % 256) + convert(num // 256) ... else: ... return "" ... >>> convert(802616035175250124568770929992) 'Hello world!\n'
Terdapat masalah baharu sekarang: kita masih memerlukan cara untuk menyembunyikan 0 dan 1. Kita boleh mendapatkannya dengan memeriksa bilangan pembolehubah setempat dalam fungsi arbitrari:
convert = lambda num: chr(num % 256) + convert(num // 256) if num else ""
Walaupun badan fungsi kelihatan sama, _ dalam fungsi pertama bukanlah hujah, dan ia juga tidak ditakrifkan dalam fungsi, jadi Python mentafsirkannya sebagai pembolehubah global:
>>> comb = lambda f, n: f(f, n) >>> convert = lambda f, n: chr(n % 256) + f(f, n // 256) if n else "" >>> comb(convert, 802616035175250124568770929992) 'Hello world!\n'
Ini berlaku tanpa mengira sama ada _ sebenarnya ditakrifkan dalam skop global.
Melaksanakan perkara ini:
>>> (lambda f, n: f(f, n))( ... lambda f, n: chr(n % 256) + f(f, n // 256) if n else "", ... 802616035175250124568770929992) 'Hello world!\n'
Kini kita boleh menggantikan nilai fungsi dalam, dan kemudian menggunakan * untuk menghantar senarai integer yang terhasil sebagai lapan pembolehubah berasingan, kita mendapat ini:
getattr( __import__(True.__class__.__name__[1] + [].__class__.__name__[2]), ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8] )( 1, (lambda _, __: _(_, __))( lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "", 802616035175250124568770929992 ) )
Anjakan bit
Hampir sampai! Kami akan menggantikan n{1..8} pembolehubah dengan , _, , _, dsb., kerana ia menimbulkan kekeliruan dengan pembolehubah yang digunakan dalam fungsi dalaman kita. Ini tidak menyebabkan masalah sebenar, kerana peraturan skop bermakna yang betul akan digunakan. Ini juga merupakan salah satu sebab mengapa kami memfaktorkan semula 256 ke tempat _ merujuk kepada 1 dan bukannya fungsi convert() kami yang dikaburkan. Ia semakin panjang, jadi saya akan menampal separuh masa pertama sahaja:
(lambda _, __, ___, ____, _____, ______, _______, ________: getattr( __import__(True.__class__.__name__[_] + [].__class__.__name__[__]), ().__class__.__eq__.__class__.__name__[:__] + ().__iter__().__class__.__name__[_____:________] )( _, (lambda _, __, ___: _(_, __, ___))( lambda _, __, ___: chr(___ % __) + _(_, __, ___ // __) if ___ else (lambda: _).func_code.co_lnotab, _ << ________, (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __) - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______ << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) << ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) << __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______ << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) + _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ << _))) + (_____ << ______) + (_ << ___) ) ) )( *(lambda _, __, ___: _(_, __, ___))( (lambda _, __, ___: [__(___[(lambda: _).func_code.co_nlocals])] + _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else [] ), lambda _: _.func_code.co_argcount, ( lambda _: _, lambda _, __: _, lambda _, __, ___: _, lambda _, __, ___, ____: _, lambda _, __, ___, ____, _____: _, lambda _, __, ___, ____, _____, ______: _, lambda _, __, ___, ____, _____, ______, _______: _, lambda _, __, ___, ____, _____, ______, _______, ________: _ ) ) )
Hanya dua perkara lagi yang tinggal. Kita akan mulakan dengan yang mudah: 256. 256=28
, jadi kita boleh menulis semula sebagai 1 << 8 (menggunakan anjakan bit kiri), atau _ << ________ dengan pembolehubah keliru kami.
Kami akan menggunakan idea yang sama dengan 802616035175250124568770929992. Algoritma bahagi-dan-takluk yang mudah boleh memecahkannya kepada jumlah nombor yang merupakan jumlah nombor yang dianjak bersama, dan seterusnya. Sebagai contoh, jika kita mempunyai 112, kita boleh memecahkannya kepada 96 16 dan kemudian (3 << 5) (2 << 3). Saya suka menggunakan anjakan bit kerana << mengingatkan saya kepada std::cout << "foo" dalam C , atau cetak chevron (cetak >>) dalam Python, kedua-duanya adalah herring merah yang melibatkan cara lain untuk melakukan I/O.
Nombor boleh diuraikan dalam pelbagai cara; tiada satu kaedah yang betul (lagipun, kita hanya boleh membahagikannya kepada (1 << 0) (1 << 0) ..., tetapi itu tidak menarik). Kami sepatutnya mempunyai sejumlah besar sarang, tetapi masih menggunakan kebanyakan pembolehubah berangka kami. Jelas sekali, melakukan ini dengan tangan tidak menyeronokkan, jadi kami akan menghasilkan algoritma. Dalam pseudokod:
import sys sys.stdout.write("Hello world!\n")
Idea asas di sini ialah kami menguji pelbagai kombinasi nombor dalam julat tertentu sehingga kami menghasilkan dua nombor, asas dan anjakan, supaya asas << anjakan adalah paling hampir dengan num yang mungkin (iaitu kita meminimumkan perbezaan mutlaknya, perbezaan). Kami kemudian menggunakan algoritma divide-and-conquer kami untuk memecahkan best_base dan best_shift, dan kemudian ulangi prosedur pada perbezaan sehingga mencapai sifar, menjumlahkan istilah sepanjang perjalanan.
Argumen untuk julat(), span, mewakili lebar ruang carian. Ini tidak boleh terlalu besar, atau kami akan mendapat num sebagai asas kami dan 0 sebagai anjakan kami (kerana beza adalah sifar), dan kerana asas tidak boleh diwakili sebagai pembolehubah tunggal, ia akan berulang, berulang tanpa had. . Jika terlalu kecil, kita akan mendapat sesuatu seperti (1 << 0) (1 << 0) ... yang dinyatakan di atas. Dalam amalan, kami mahu rentang menjadi lebih kecil apabila kedalaman rekursi meningkat. Melalui percubaan dan kesilapan, saya mendapati persamaan ini berfungsi dengan baik:
span=⌈log1.5|bilangan|⌉ ⌊24−dalam⌋
Menterjemahkan pseudokod ke dalam Python dan membuat beberapa tweak (sokongan untuk hujah mendalam dan beberapa kaveat yang melibatkan nombor negatif), kami mendapat ini:
(lambda _, __, ___, ____, _____, ______, _______, ________: getattr( __import__(True.__class__.__name__[_] + [].__class__.__name__[__]), ().__class__.__eq__.__class__.__name__[:__] + ().__iter__().__class__.__name__[_____:________] )( _, (lambda _, __, ___: _(_, __, ___))( lambda _, __, ___: chr(___ % __) + _(_, __, ___ // __) if ___ else (lambda: _).func_code.co_lnotab, _ << ________, (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __) - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______ << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) << ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) << __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______ << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) + _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) << (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ << _))) + (_____ << ______) + (_ << ___) ) ) )( *(lambda _, __, ___: _(_, __, ___))( (lambda _, __, ___: [__(___[(lambda: _).func_code.co_nlocals])] + _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else [] ), lambda _: _.func_code.co_argcount, ( lambda _: _, lambda _, __: _, lambda _, __, ___: _, lambda _, __, ___, ____: _, lambda _, __, ___, ____, _____: _, lambda _, __, ___, ____, _____, ______: _, lambda _, __, ___, ____, _____, ______, _______: _, lambda _, __, ___, ____, _____, ______, _______, ________: _ ) ) )
Sekarang, apabila kita memanggil convert(802616035175250124568770929992), kita mendapat penguraian yang bagus:
import sys sys.stdout.write("Hello world!\n")
Lekatkan ini sebagai pengganti 802616035175250124568770929992, dan satukan semua bahagian:
import os os.write(1, "Hello world!\n")
Dan begitulah.
Tambahan: Sokongan Python 3
Sejak menulis siaran ini, beberapa orang telah bertanya tentang sokongan Python 3. Saya tidak memikirkannya pada masa itu, tetapi apabila Python 3 terus mendapat daya tarikan (dan terima kasih untuk itu!), siaran ini jelas sudah lama tertunda untuk kemas kini.
Nasib baik, Python 3 (setakat penulisan, 3.6) tidak memerlukan kami untuk mengubah banyak:
__import__("os").write(1, "Hello world!\n")
Berikut ialah versi penuh Python 3:
getattr(__import__("os"), "write")(1, "Hello world!\n")
Terima kasih kerana membaca! Saya terus kagum dengan populariti siaran ini.
Atas ialah kandungan terperinci Mengelirukan 'Hello dunia!' mengaburkan pada Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!