Apa tips umum yang Anda miliki untuk bermain golf di Python? Saya mencari ide yang dapat diterapkan pada masalah kode-golf dan yang juga setidaknya agak spesifik untuk Python (mis. "Hapus komentar" bukan jawaban).
Silakan kirim satu tip per jawaban.
Apa tips umum yang Anda miliki untuk bermain golf di Python? Saya mencari ide yang dapat diterapkan pada masalah kode-golf dan yang juga setidaknya agak spesifik untuk Python (mis. "Hapus komentar" bukan jawaban).
Silakan kirim satu tip per jawaban.
Jawaban:
Gunakan a=b=c=0sebagai ganti a,b,c=0,0,0.
Gunakan a,b,c='123'sebagai ganti a,b,c='1','2','3'.
Persyaratan bisa panjang. Dalam beberapa kasus, Anda dapat mengganti kondisional sederhana dengan (a,b)[condition]. Jika conditionbenar, maka bdikembalikan.
Membandingkan
if a<b:return a
else:return b
Untuk ini
return(b,a)[a<b]
a if a<b else bdana<b and a or b
(lambda(): b, lambda(): a)[a < b]()buat hubungan arus pendek Anda sendiri dengan lambdas
P and A or Buntuk setiap A yang memberi bool(A)=False. Tetapi (P and [A] or [B])[0]akan melakukan pekerjaan. Lihat diveintopython.net/power_of_introspection/and_or.html untuk referensi.
Suatu hal hebat yang pernah saya lakukan adalah:
if 3 > a > 1 < b < 5: foo()
dari pada:
if a > 1 and b > 1 and 3 > a and 5 > b: foo()
Operator perbandingan Python mengguncang.
Menggunakan semuanya sebanding dengan Python 2, Anda juga dapat menghindari andoperator dengan cara ini. Sebagai contoh, jika a, b, cdan dadalah bilangan bulat,
if a<b and c>d:foo()
dapat disingkat oleh satu karakter ke:
if a<b<[]>c>d:foo()
Ini menggunakan bahwa setiap daftar lebih besar dari bilangan bulat apa pun.
Jika cdan dadalah daftar, ini menjadi lebih baik:
if a<b<c>d:foo()
3>a>1<b<5
[$a => $b]->[$b <= $a]:)
if(a<b)+(c>d):foo()
*. An orakan menjadi+
foo()if 3>a>1<b<5
Jika Anda menggunakan fungsi bawaan berulang kali, mungkin akan lebih hemat tempat untuk memberinya nama baru, jika menggunakan argumen yang berbeda:
r=range
for x in r(10):
for y in r(100):print x,y
Terkadang kode Python Anda mengharuskan Anda memiliki 2 tingkat lekukan. Yang jelas harus dilakukan adalah menggunakan satu dan dua ruang untuk setiap level indentasi.
Namun, Python 2 menganggap tab dan karakter spasi sebagai level indentasi yang berbeda.
Ini berarti level indentasi pertama dapat berupa satu spasi dan yang kedua dapat menjadi satu karakter tab.
Sebagai contoh:
if 1:
if 1:
\tpass
Di mana \tkarakter tab.
TabError: inconsistent use of tabs and spaces in indentation.
Gunakan substitusi string dan execuntuk berurusan dengan kata kunci panjang seperti lambdayang sering diulang dalam kode Anda.
a=lambda b:lambda c:lambda d:lambda e:lambda f:0 # 48 bytes (plain)
exec"a=`b:`c:`d:`e:`f:0".replace('`','lambda ') # 47 bytes (replace)
exec"a=%sb:%sc:%sd:%se:%sf:0"%(('lambda ',)*5) # 46 bytes (%)
String target sangat sering 'lambda ', yaitu 7 byte. Misalkan cuplikan kode Anda berisi nkejadian 'lambda ', dan spanjang byte. Kemudian:
plainpilihan adalah sbyte panjang.replacepilihan adalah s - 6n + 29byte panjang.%pilihan adalah s - 5n + 22 + len(str(n))byte panjang.Dari sebidang byte yang disimpanplain untuk ketiga opsi ini, kita dapat melihat bahwa:
exec"..."%(('lambda ',)*5)menyimpan 2 byte, dan merupakan pilihan terbaik Anda.exec"...".replace('`','lambda ')adalah pilihan terbaik Anda.Untuk kasus lain, Anda dapat mengindeks tabel di bawah ini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 (occurences)
+---------------------------------------------------------
3 | - - - - - - - - - - - - - - r r r r r
4 | - - - - - - - - - r r r r r r r r r r
5 | - - - - - - - r r r r r r r r r r r r
6 | - - - - - r r r r r r r r r r r r r r
7 | - - - - % r r r r r r r r r r r r r r
8 | - - - % % r r r r r r r r r r r r r r
9 | - - - % % r r r r r r r r r r r r r r
10 | - - % % % r r r r r r r r r r r r r r
11 | - - % % % r r r r r r r r r r r r r r
12 | - - % % % r r r r r r r r r r r r r r r = replace
13 | - - % % % r r r r r r r r r r r r r r % = string %
14 | - % % % % r r r r r r r r r r r r r r - = do nothing
15 | - % % % % r r r r r r r r r r r r r r
(length)
Misalnya, jika string lambda x,y:(panjang 11) muncul 3 kali dalam kode Anda, Anda lebih baik menulis exec"..."%(('lambda x,y:',)*3).
replacesangat besar.
=>hanya string = lambda . Misalnya, f=>:0akan f = lambda: 0.
Gunakan pengirisan panjang untuk memilih satu string dari banyak string
>>> for x in 0,1,2:print"fbboaaorz"[x::3]
...
foo
bar
baz
vs.
>>> for x in 0,1,2:print["foo","bar","baz"][x]
...
foo
bar
baz
Dalam case dua-string Boolean ini, kita juga bisa menulis
b*"string"or"other_string"
untuk
["other_string","string"][b]
Tidak seperti interleaving, ini bekerja untuk string dengan panjang berapa pun, tetapi dapat memiliki masalah prioritas operator jika bbukan ekspresi.
for x in ("foo","bar","baz"): print x
xyang diberikan. Bagian golf adalah "fbboaaorz"[x::3]vs ["foo","bar","baz"][x]Bagaimana xnilai didapat akan menjadi bagian lain dari solusi golf Anda.
Gunakan `n`untuk mengonversi bilangan bulat ke string alih-alih menggunakan str(n):
>>> n=123
>>> `n`
'123'
Katakanlah Anda ingin meng-hardcode tabel pencarian Boolean, seperti yang mana dari dua belas angka pertama bahasa Inggris yang mengandung n.
0: False
1: True
2: False
3: False
4: False
5: False
6: False
7: True
8: False
9: True
10:True
11:True
12:False
Kemudian, Anda bisa mengimplementasikan tabel pencarian ini dengan ringkas sebagai:
3714>>i&1
dengan hasil 0atau 1sama Falsedengan True.
Idenya adalah bahwa angka ajaib menyimpan tabel sebagai bitstring bin(3714)= 0b111010000010, dengan ndigit -th (dari ujung) yang sesuai dengan nentri tabel th. Kami mengakses nentri th dengan bithifting nruang angka ke kanan dan mengambil digit terakhir &1.
Metode penyimpanan ini sangat efisien. Bandingkan dengan alternatifnya
n in[1,7,9,10,11]
'0111010000010'[n]>'0'
Anda dapat membuat tabel pencarian Anda menyimpan entri multibit yang dapat diekstraksi seperti
340954054>>4*n&15
untuk mengekstrak blok empat-bit yang relevan.
Katakanlah Anda melakukan iterasi pada sel-sel m*ngrid Alih-alih dua forloop bersarang , satu untuk baris dan satu kolom, biasanya lebih pendek untuk menggunakan satu loop untuk beralih di atas m*nsel - sel grid. Anda bisa mengekstrak baris dan kolom sel di dalam loop.
Kode asli:
for i in range(m):
for j in range(n):
do_stuff(i,j)
Kode golf:
for k in range(m*n):
do_stuff(k/n,k%n)
Akibatnya, Anda mengulangi produk Cartesian dari dua rentang, menyandikan pasangan (i,j)sebagai x=i*n+j. Anda telah menyimpan rangepanggilan mahal dan tingkat lekukan di dalam loop. Urutan iterasi tidak berubah.
Gunakan //daripada /di Python 3. Jika Anda merujuk idan jberkali-kali, mungkin lebih cepat untuk menetapkan nilainya i=k/n, j=k%ndi dalam loop.
for i in range(m*n*o): do_stuff(i/n/o,i%(n*o)/o,i%o)
nloop: repl.it/EHwa
itertools.productbisa jauh lebih ringkas daripada loop bersarang, terutama ketika menghasilkan produk cartesian. a1, a2, b1, b2adalah contoh dari produk kartesius 'ab'dan'12'
Kecuali jika token berikut dimulai dengan eatau E. Anda dapat menghapus ruang mengikuti nomor.
Misalnya:
if i==4 and j==4:
pass
Menjadi:
if i==4and j==4:
pass
Menggunakan ini dalam pernyataan satu baris yang rumit dapat menyimpan beberapa karakter.
EDIT: seperti yang ditunjukkan @marcog, 4or aakan berfungsi, tetapi tidak a or4karena ini akan membingungkan dengan nama variabel.
if(i,j)==(4,4):bahkan lebih pendek dan dalam kasus khusus iniif i==j==4:
4or abekerja, tetapi tidaka or4
0orjuga tidak berfungsi ( 0omerupakan awalan untuk angka oktal).
0 or xselalu akan kembali x. Mungkin juga memotong 0 or.
0orbaik-baik saja sebagai bagian dari angka yang lebih lama. 10 or xsetara dengan 10or x.
Untuk integer n, Anda bisa menulis
n+1 sebagai -~nn-1 sebagai ~-n karena bit flip ~xsama dengan -1-x. Ini menggunakan jumlah karakter yang sama, tetapi secara tidak langsung dapat memotong spasi atau paren sebagai prioritas operator.
Membandingkan:
while n-1: #Same as while n!=1
while~-n:
c/(n-1)
c/~-n
or f(n)+1
or-~f(n)
(n-1)/10+(n-1)%10
~-n/10+~-n%10
Operator ~dan unary -yang diutamakan lebih tinggi dari *, /, %, tidak seperti biner +.
-~-xmenghemat satu byte vs (1-x).
a+b+1dapat ditulis secara lebih ringkas a-~b.
n-i-1itu adil n+~i.
Cara yang bagus untuk mengonversi iterable ke daftar di Python 3 :
bayangkan Anda memiliki beberapa iterable, like
i = (1,2,3,4)
i = range(4)
i = (x**2 for x in range(5))
Tetapi Anda perlu daftar:
x=list(i) #the default way
*x,=i #using starred assignment -> 4 char fewer
Sangat berguna untuk membuat daftar karakter dari string
s=['a','b','c','d','e']
s=list('abcde')
*s,='abcde'
*s,='abcde'dan kemudian scrash python3 interaktif saya dengan segfault :(
[*'abcde'].
Alih-alih range(x), Anda dapat menggunakan *operator pada daftar apa pun, jika Anda tidak benar-benar perlu menggunakan nilai i:
for i in[1]*8:pass
sebagai lawan
for i in range(8):pass
Jika Anda perlu melakukan ini lebih dari dua kali, Anda bisa menetapkan setiap iterable ke variabel, dan gandakan variabel itu dengan rentang yang Anda inginkan:
r=1,
for i in r*8:pass
for i in r*1000:pass
Catatan : ini seringkali lebih lama daripada exec"pass;"*8, jadi trik ini seharusnya hanya digunakan ketika itu bukan pilihan.
[1]*8lebih pendek daripada range(8), Anda juga bisa menghemat ruang karena for i in[...legal sementara for i in range...tidak".
exec"pass;"*8secara signifikan lebih pendek.
r=1, r*8adalah 8, dan Anda tidak dapat mengulangi melalui nomor. Saya kira Anda maksudr=[1]
Anda dapat menggunakan wajah smiley alien yang lama untuk membalik urutan:
[1, 2, 3, 4][::-1] # => [4, 3, 2, 1]
Cara terbaik untuk menjelaskan ini adalah melalui contoh:
>>> a,*b,c=range(5)
>>> a
0
>>> b
[1, 2, 3]
>>> c
4
Kami telah melihat manfaatnya - mengubah iterable menjadi daftar dengan Python 3 :
a=list(range(10))
*a,=range(10)
Berikut adalah beberapa kegunaan lagi.
a=L[-1]
*_,a=L
Dalam beberapa situasi, ini juga dapat digunakan untuk mendapatkan elemen pertama untuk menghemat parens:
a=(L+[1])[0]
a,*_=L+[1]
a=1;b=2;c=[]
a,b,*c=1,2
_,*L=L
*L,_=L
Ini lebih pendek dari alternatif L=L[1:]dan L.pop(). Hasilnya juga dapat disimpan ke daftar yang berbeda.
Kiat milik @grc
a=1;L=[]berkali-kali. Sungguh menakjubkan bahwa Anda dapat menyimpan karakter pada sesuatu yang sangat mudah seperti ini.
a,*L=1,), tetapi masih menyimpan satu char :)
a,*_,b=L
Anda dapat menulis set seperti ini S={1,2,3}Ini juga berarti Anda dapat memeriksa keanggotaan menggunakan {e}&Salih-alih e in Syang menyimpan satu karakter.
ifs karena tidak ada spasi ( if{e}&S:)
not indengan {e}-Sdengan trik yang
Selama berabad-abad itu mengganggu saya bahwa saya tidak bisa memikirkan cara singkat untuk mendapatkan seluruh alfabet. Jika Anda menggunakan rangecukup yang R=rangelayak dimiliki dalam program Anda, maka
[chr(i+97)for i in R(26)]
lebih pendek dari yang naif
'abcdefghijklmnopqrstuvwxyz'
, tapi selain itu lebih panjang dengan satu karakter. Itu menghantui saya bahwa yang pandai yang membutuhkan pengetahuan tentang nilai-nilai ascii akhirnya menjadi lebih bertele-tele daripada hanya mengetik semua huruf.
Sampai saya melihat jawaban ini untuk My Daughter's Alphabet . Saya tidak bisa mengikuti riwayat sunting dengan cukup baik untuk mengetahui apakah jenius ini adalah karya OP atau apakah itu adalah saran dari seorang komentator, tetapi ini (saya percaya) adalah cara terpendek untuk membuat iterable dari 26 huruf dalam alfabet Romawi.
map(chr,range(97,123))
Jika huruf besar tidak penting, Anda dapat menghapus karakter lain dengan menggunakan huruf besar:
map(chr,range(65,91))
Saya menggunakan mapterlalu banyak, saya tidak tahu bagaimana ini tidak pernah terjadi pada saya.
string.lowercase- itulah gunanya .
ord('z'))? Selain panjangnya sama ... Selain itu, jika Anda membutuhkan alfanumerik, ganti str.isalphadengan versi @ quintopia dengan str.isalnum. (Tetapi jika Anda hanya membutuhkan satu kasing, seluruh string 36-karakter tidak lebih dari filter(str.isalnum,map(chr,range(90))).)
R, versi saya lebih pendek daripada yang asli: '%c'*26%tuple(R(97,123))(hanya 24 karakter) jika Anda mengeja rangeitu asalkan alfabet - versi huruf besar lebih pendek
Meskipun python tidak memiliki pernyataan peralihan, Anda dapat meniru mereka dengan kamus. Misalnya, jika Anda menginginkan sakelar seperti ini:
switch (a):
case 1:
runThisCode()
break
case 2:
runThisOtherCode()
break
case 3:
runThisOtherOtherCode()
break
Anda bisa menggunakan ifpernyataan, atau Anda bisa menggunakan ini:
exec{1:"runThisCode()",2:"runThisOtherCode()",3:"runThisOtherOtherCode()"}[a]
atau ini:
{1:runThisCode,2:runThisOtherCode,3:runThisOtherOtherCode}[a]()
yang lebih baik jika semua jalur kode berfungsi dengan parameter yang sama.
Untuk mendukung nilai default, lakukan ini:
exec{1:"runThisCode()"}.get(a,"defaultCode()")
(atau ini:)
{1:runThisCode}.get(a,defaultCode)()
Satu keuntungan lain dari hal ini adalah bahwa jika Anda memang memiliki kelebihan, Anda bisa menambahkannya setelah akhir kamus:
exec{'key1':'code','key2':'code'}[key]+';codeThatWillAlwaysExecute'
Dan jika Anda hanya ingin menggunakan sakelar untuk mengembalikan nilai:
def getValue(key):
if key=='blah':return 1
if key=='foo':return 2
if key=='bar':return 3
return 4
Anda bisa melakukan ini:
getValue=lambda key:{'blah':1,'foo':2,'bar',3}.get(key,4)
dict(s1=v1,s2=v2,...,sn=vn)alih-alih {'s1':v1,'s2':v2,...,'sn':vn}menyimpan 2 * n-4 byte dan lebih baik jika n> = 3
Ketika Anda memiliki dua nilai boolean, adan b, jika Anda ingin mengetahui apakah keduanya adan bitu benar, gunakan *sebagai ganti and:
if a and b: #7 chars
vs.
if a*b: #3 chars
jika salah satu nilai salah, itu akan mengevaluasi seperti 0dalam pernyataan itu, dan nilai integer hanya benar jika bukan nol.
&: a=b=False,a&b
+untuk orjika Anda bisa menjamina != -b
|berfungsi dalam semua situasi.
*bukannya and/ &&menyimpan beberapa byte dalam banyak bahasa.
Python 2 memungkinkan Anda mengonversi objek xke representasi string `x`dengan biaya hanya 2 karakter. Gunakan ini untuk tugas-tugas yang lebih mudah dilakukan pada string objek daripada objek itu sendiri.
Gabung karakter
Diberikan daftar karakter l=['a','b','c'], seseorang dapat menghasilkan ''.join(l)sebagai `l`[2::5], yang menyimpan byte.
Alasannya adalah bahwa `l`ini "['a', 'b', 'c']"(dengan spasi), sehingga seseorang dapat mengekstrak huruf dengan daftar slice, mulai bahwa karakter nol-diindeks kedua a, dan mengambil setiap karakter kelima dari sana. Ini tidak berfungsi untuk bergabung dengan string multi-karakter atau karakter melarikan diri yang diwakili seperti '\n'.
Digit gabungan
Demikian pula, diberikan daftar non-kosong digit seperti l=[0,3,5], seseorang dapat menggabungkannya ke dalam string '035'sebagai `l`[1::3].
Ini menghemat melakukan sesuatu seperti map(str,l). Perhatikan bahwa mereka harus berupa digit tunggal, dan tidak dapat mengapung seperti 1.0dicampur. Juga, ini gagal pada daftar kosong, menghasilkan ].
Periksa negatif
Sekarang, untuk tugas non-string. Misalkan Anda memiliki daftar lbilangan real dan ingin menguji apakah itu berisi bilangan negatif, menghasilkan Boolean.
Anda dapat melakukan
'-'in`l`
yang memeriksa tanda negatif pada rep string. Ini lebih pendek dari salah satu dari
any(x<0for x in l)
min(l+[0])<0
Untuk yang kedua, min(l)<0akan gagal pada daftar kosong, jadi Anda harus melakukan lindung nilai.
str(l)[2::5]adalah 12 byte, berbanding 19 untuk ''.join(map(str,l)). Situasi aktual di mana ini muncul (di mana lpernyataan generator, bukan daftar) menyelamatkan saya hanya satu byte ... yang masih layak!
Fungsi satu baris dapat dilakukan dengan lambda:
def c(a):
if a < 3: return a+10
else: return a-5
dapat dikonversi ke (perhatikan ruang yang hilang 3anddan 10or)
c=lambda a:a<3and a+10or a-5
c=lambda a:a+[-5,10][a<3]. trik dan / atau lebih berguna ketika Anda bergantung pada perilaku shortcircuit
else: bisa dijatuhkan sebagai returnmenghentikan eksekusi fungsi, jadi semua yang mengikuti hanya dieksekusi jika ifkondisinya gagal, alias jika elsekondisinya benar. Dengan demikian elsedapat dihilangkan dengan aman. (Dijelaskan secara terperinci untuk orang-orang baru di luar sana)
c=lambda a:a-5+15*(a<3)
Jika Anda ingin mendapatkan hasil pembulatan untuk divisi, seperti yang Anda lakukan dengan //lantai, Anda bisa menggunakan math.ceil(3/2)untuk 15 atau yang lebih pendek -(-3//2)untuk 8 byte.
math.floor(n) : 13 bytes+12 for import
n//1 : 4 bytes
math.ceil(n) : 12 bytes+12 for import
-(-n//1) : 8 bytes
n//1+1bukannya ceil tetapi itu berarti ceil (n) = n +1 tetapi itu harus bekerja untuk semua nilai non integer
round(x)adalah (x+.5)//1, +1 byte tetapi yang terakhir dimulai dengan a (, dan jika xjumlah yang terdiri dari konstanta dapat berguna.
+=sebagai ganti appenddanextendA.append(B)
dapat disingkat menjadi:
A+=B,
B,di sini membuat tupel satu elemen yang dapat digunakan untuk memperluas Aseperti [B]di A+=[B].
A.extend(B)
dapat disingkat menjadi:
A+=B
return 0atau return 1setara dengan return Falseatau return True.
-xbukannya x*-1. --8.32bukannya -8.32*-1. Atau hanya 8.32...
A+=B Badalah a tuple.
Memilih satu dari dua angka berdasarkan suatu syarat
Anda sudah tahu untuk menggunakan pemilihan daftar [x,y][b]dengan Boolean buntuk ekspresi ternary y if b else x. Variabel x,, ydan bjuga bisa menjadi ekspresi, meskipun perhatikan bahwa keduanya xdan ydievaluasi bahkan ketika tidak dipilih.
Berikut beberapa potensi pengoptimalan kapan xdan ysedang angka.
[0,y][b] -> y*b [1,y][b] -> y**b [x,1][b] -> b or x[x,x+1][b] -> x+b[x,x-1][b] -> x-b[1,-1][b] -> 1|-b[x,~x][b] -> x^-b[x,y][b] -> x+z*b(atau y-z*b), di mana z = yx.Anda juga dapat beralih xdan yjika Anda dapat menulis ulang bmenjadi negasi sebagai gantinya.
Dengan dirilisnya Python 3.5 , manipulasi daftar, tupel, set, dan dikt baru saja menjadi pemain golf.
Bandingkan pasangan:
set(T)
{*T}
list(T)
[*T]
tuple(T)
(*T,)
Jauh lebih pendek! Namun, perlu diketahui bahwa jika Anda hanya ingin mengonversi sesuatu ke daftar dan menetapkannya ke variabel, pembongkaran yang dapat diperpanjang yang normal lebih pendek:
L=[*T]
*L,=T
Sintaks yang sama berfungsi untuk tupel:
T=*L,
yang seperti diperpanjang membongkar iterable, tetapi dengan tanda bintang dan koma di sisi lain.
Pembongkaran sedikit lebih pendek daripada penggabungan jika Anda perlu menambahkan daftar / tuple ke kedua sisi:
[1]+T+[2]
[1,*T,2]
(1,)+T+(2,)
(1,*T,2)
Ini tidak terbatas pada print, tapi itu pasti dari mana sebagian besar jarak tempuh akan datang. PEP448 sekarang memungkinkan untuk beberapa pembongkaran, seperti:
>>> T = (1, 2, 3)
>>> L = [4, 5, 6]
>>> print(*T,*L)
1 2 3 4 5 6
Ini mungkin tidak akan sering terjadi, tetapi sintaksis dapat digunakan untuk menghemat memperbarui kamus jika Anda memperbarui setidaknya tiga item:
d[0]=1;d[1]=3;d[2]=5
d={**d,0:1,1:3,2:5}
Ini pada dasarnya meniadakan kebutuhan dict.update.
>>> for i in range(x):s+=input()
jika nilai saya tidak berguna:
>>> for i in[0]*x:s+=input()
atau
>>> exec's+=input();'*x
for i in[0]*x:s+=input()untuk menghemat ruang lain. Selain itu, Anda dapat menghapus spasi antara eksekutif dan tanda kutip pertama untuk mendapatkannyaexec's+=input();'*x
for i in[0]*x:s+=input()