




Artikel ini membahas konversi yang paling menarik yang dilakukan oleh dua rantai
transponder (yang pertama menerjemahkan kode Python menjadi kode dalam
bahasa pemrograman baru 11l , dan yang kedua menerjemahkan kode menjadi 11l dalam C ++), dan juga membandingkan kinerja dengan alat akselerasi lainnya / Eksekusi kode python (PyPy, Cython, Nuitka).
Mengganti irisan \ irisan dengan rentang
Indikasi eksplisit untuk pengindeksan dari akhir array
s[(len)-2]
bukan hanya
s[-2]
diperlukan untuk menghilangkan kesalahan berikut:
- Ketika, misalnya, diperlukan untuk mendapatkan karakter sebelumnya dengan
s[i-1]
, tetapi untuk i = 0 seperti / catatan ini bukannya kesalahan akan diam-diam mengembalikan karakter terakhir dari string [ dan dalam praktiknya saya menemukan kesalahan seperti itu - komit ] . - Ekspresi
s[i:]
setelah i = s.find(":")
akan bekerja secara tidak benar ketika karakter tidak ditemukan dalam string [ bukan '' bagian dari string yang dimulai dari karakter pertama :
dan kemudian '' karakter terakhir dari string akan diambil ] (dan umumnya , Saya pikir mengembalikan -1
dengan fungsi find()
di Python juga salah [ harus mengembalikan null / Tidak ada [ dan jika -1 diperlukan, itu harus ditulis secara eksplisit: i = s.find(":") ?? -1
] ] ) - Menulis
s[-n:]
untuk mendapatkan n
karakter terakhir dari string tidak akan berfungsi dengan benar ketika n = 0.
Rantai operator pembanding
Sepintas, ini adalah fitur yang luar biasa dari bahasa Python, tetapi dalam praktiknya itu dapat dengan mudah ditinggalkan / dibagikan dengan menggunakan operator dan rentang:
Daftar pemahaman
Demikian pula, ternyata, Anda dapat menolak fitur menarik lain dari Python - daftar pemahaman.
Sementara beberapa
mengagungkan pemahaman daftar dan bahkan menyarankan untuk meninggalkan `filter ()` dan `map ()` , saya menemukan bahwa:
Di semua tempat di mana saya melihat pemahaman daftar Python, Anda dapat dengan mudah bertahan dengan fungsi `filter ()` dan `map ()`. dirs[:] = [d for d in dirs if d[0] != '.' and d != exclude_dir] dirs[:] = filter(lambda d: d[0] != '.' and d != exclude_dir, dirs) '[' + ', '.join(python_types_to_11l[ty] for ty in self.type_args) + ']' '[' + ', '.join(map(lambda ty: python_types_to_11l[ty], self.type_args)) + ']'
`filter ()` dan `map ()` dalam 11l terlihat lebih cantik daripada di Python dirs[:] = filter(lambda d: d[0] != '.' and d != exclude_dir, dirs) dirs = dirs.filter(d -> d[0] != '.' & d != @exclude_dir) '[' + ', '.join(map(lambda ty: python_types_to_11l[ty], self.type_args)) + ']' '['(.type_args.map(ty -> :python_types_to_11l[ty]).join(', '))']' outfile.write("\n".join(x[1] for x in fileslist if x[0])) outfile.write("\n".join(map(lambda x: x[1], filter(lambda x: x[0], fileslist)))) outfile.write(fileslist.filter(x -> x[0]).map(x -> x[1]).join("\n"))
dan akibatnya, kebutuhan untuk pemahaman daftar dalam 11l benar-benar menghilang [ penggantian pemahaman daftar dengan filter()
dan / atau map()
dilakukan selama konversi kode Python ke 11l secara otomatis ] .
Ubah rantai if-elif-else untuk beralih
Sementara Python tidak mengandung pernyataan switch, ini adalah salah satu konstruksi paling indah di 11l, dan jadi saya memutuskan untuk memasukkan switch secara otomatis:
Untuk kelengkapan, berikut adalah kode C ++ yang dihasilkan switch (instr[i]) { case u'[': nesting_level++; break; case u']': if (--nesting_level == 0) goto break_; break; case u''': ending_tags.append(u"'"_S); break; // '' case u''': assert(ending_tags.pop() == u'''); break; }
Ubah kamus kecil menjadi kode asli
Pertimbangkan baris kode Python ini:
tag = {'*':'b', '_':'u', '-':'s', '~':'i'}[prev_char()]
Kemungkinan besar, bentuk rekaman ini tidak terlalu efektif
[ dalam hal kinerja ] , tetapi sangat nyaman.
Dalam 11l, entri yang sesuai dengan baris ini
[ dan diperoleh oleh transporter Python → 11l ] tidak hanya nyaman
[ namun, tidak begitu elegan seperti pada Python ] , tetapi juga cepat:
var tag = switch prev_char() {'*' {'b'}; '_' {'u'}; '-' {'s'}; '~' {'i'}}
Baris di atas diterjemahkan ke dalam:
auto tag = [&](const auto &a){return a == u'*' ? u'b'_C : a == u'_' ? u'u'_C : a == u'-' ? u's'_C : a == u'~' ? u'i'_C : throw KeyError(a);}(prev_char());
[ Panggilan fungsi lambda akan dikompilasi oleh kompiler C ++ \ inline selama proses optimasi dan hanya rantai operator yang akan tetap ?/:
]Dalam kasus ketika suatu variabel ditugaskan, kamus dibiarkan apa adanya:
Capture \ Ca pture variabel eksternal
Dalam Python, untuk menunjukkan bahwa suatu variabel bukan lokal, tetapi harus diambil di luar
[ dari fungsi saat ini ] , kata kunci nonlokal digunakan
[ jika tidak, misalnya, found = True
akan diperlakukan sebagai membuat variabel lokal baru yang found
, daripada menetapkan nilai yang sudah variabel eksternal yang ada ] .
Di 11l, awalan @ digunakan untuk ini:
C ++:
auto writepos = 0; auto write_to_pos = [..., &outfile, &writepos](const auto &pos, const auto &npos) { outfile.write(...); writepos = npos; };
Variabel global
Mirip dengan variabel eksternal, jika Anda lupa mendeklarasikan variabel global dalam Python
[ menggunakan kata kunci global ] , Anda mendapatkan bug yang tidak terlihat:
Kode 11l
[ kanan ] , tidak seperti Python
[ kiri ], akan
break_label_index
kesalahan 'variabel
break_label_index
tidak dideklarasikan' pada
break_label_index
kompilasi.
Indeks / nomor item kontainer saat ini
Saya terus lupa urutan variabel yang mengembalikan fungsi Python
enumerate
{nilai lebih dulu, lalu indeks, atau sebaliknya}. Perilaku analog di Ruby -
each.with_index
- lebih mudah diingat: dengan indeks berarti indeks datang setelah nilai, bukan sebelumnya. Tetapi dalam 11l, logika bahkan lebih mudah diingat:
Performa
Program untuk mengubah markup PC ke HTML digunakan sebagai
program pengujian, dan kode sumber untuk
artikel pada markup PC digunakan sebagai sumber data
[ karena artikel ini saat ini yang terbesar dari yang ditulis pada markup PC ] , dan diulang 10 kali, yaitu diperoleh dari file artikel 48,8 kilobyte ukuran 488Kb.
Berikut adalah diagram yang menunjukkan berapa kali cara yang sesuai untuk mengeksekusi kode Python lebih cepat daripada implementasi asli
[ CPython ] :

Dan sekarang tambahkan ke diagram implementasi yang dihasilkan oleh Python → 11l → C ++ transpiler:
Waktu
konversi file [ 488Kb ] runtime adalah 868 ms untuk CPython dan 38 ms untuk kode C ++ yang dihasilkan
[ kali ini termasuk [ i.e. tidak hanya bekerja dengan data dalam RAM ] menjalankan program dengan sistem operasi dan semua input / output [ membaca file sumber [ .pq ] dan menyimpan file baru [ .html ] ke disk ] ] .
Saya juga ingin mencoba
Shed Skin , tetapi tidak mendukung fungsi lokal.
Numba juga tidak dapat digunakan (ia melempar kesalahan 'Penggunaan opcode yang tidak diketahui LOAD_BUILD_CLASS').
Berikut adalah arsip dengan program yang digunakan untuk membandingkan kinerja
[di bawah Windows ] (membutuhkan Python 3.6 atau lebih tinggi dan paket Python berikut: pywin32, cython).
Kode sumber dalam Python dan output dari transponder Python -> 11l dan 11l -> C ++: