Kiat Golf di ECMAScript 6 ke atas


88

Ini mirip dengan "Tips untuk bermain golf di <...>" lainnya, tetapi secara khusus menargetkan fitur-fitur baru dalam JavaScript yang dibesarkan dalam ECMAScript 6 ke atas.

JavaScript inheren adalah bahasa yang sangat verbose, function(){}, .forEach(), mengkonversi string untuk array, array objek seperti ke array, dll, dll super bloats dan tidak sehat bagi Golf.

ES6 +, di sisi lain, memiliki beberapa fitur yang sangat berguna dan mengurangi jejak. x=>y,, [...x]dll. hanyalah beberapa contoh.

Silakan kirim beberapa trik bagus yang dapat membantu mengurangi beberapa byte tambahan dari kode Anda.

CATATAN: Trik untuk ES5 sudah tersedia di Tips untuk bermain golf di JavaScript ; jawaban utas ini harus fokus pada trik yang hanya tersedia di ES6 dan versi ES lainnya di masa depan.

Namun, utas ini juga untuk pengguna yang saat ini golf menggunakan fitur ES5. Jawaban mungkin juga berisi tips untuk membantu mereka memahami dan memetakan fitur ES6 dengan gaya pengkodean ES5.

Jawaban:


42

Operator yang tersebar ...

Operator spread mengubah nilai array menjadi daftar yang dipisahkan koma.

Gunakan case 1:

Langsung menggunakan array di mana fungsi mengharapkan daftar

list=[1,2,3]
x=Math.min(...list)
list=[10,20], a.push(...list) // similar to concat()

Gunakan case 2:

Buat array literal dari iterable (biasanya string)

[...'buzzfizz'] // -> same as .split('')

Gunakan case 3:

Menyatakan sejumlah variabel argumen untuk suatu fungsi

F=(...x) => x.map(v => v+1)
// example: F(1,2,3) == [2,3,4]

Lihat mozilla doc


3
Sekarang saya punya downvote di sini. Jelas seseorang mencatat sesuatu yang sangat salah dalam tip ini, terlalu malu untuk meninggalkan komentar dan menjelaskan apa ...
edc65

Itu terlihat baik-baik saja. Mungkin karena kurangnya titik koma? ;) (btw, Anda juga dapat menggunakannya sebagai parameter istirahat, seperti percikan di Ruby)
gcampbell

Anda dapat menambahkan bahwa itu juga memiliki kasus penggunaan dalam tanda tangan fungsi :)
Felix Dombek

Misclick tidak bermaksud mengalah
Stan Strum

@Stan Strum itu terjadi. Saya akan membuat pembaruan kecil untuk posting ini sehingga Anda akhirnya dapat mengubah suara Anda (atau Anda sudah melakukannya?)
edc65

21

Trik dipelajari di sini sejak saya bergabung

Bahasa pemrograman utama saya adalah JS dan kebanyakan ES6. Sejak saya bergabung dengan situs ini seminggu yang lalu, saya telah belajar banyak trik berguna dari sesama anggota. Saya menggabungkan beberapa di sini. Semua kredit untuk komunitas.

Fungsi panah dan loop

Kita semua tahu bahwa fungsi panah menyimpan banyak byts

function A(){do something} // from this
A=a=>do something // to this

Tetapi Anda harus mengingat beberapa hal

  • Cobalah untuk club beberapa pernyataan menggunakan ,ie (a=b,a.map(d))- Di sini, nilai yang dikembalikan adalah ekspresi terakhira.map(d)
  • jika do somethingbagian Anda lebih dari satu pernyataan, maka Anda perlu menambahkan {}tanda kurung di sekitarnya .
  • Jika ada {}tanda kurung di sekitarnya , Anda perlu menambahkan pernyataan pengembalian eksplisit.

Hal di atas berlaku banyak kali ketika Anda memiliki loop yang terlibat. Jadi sesuatu seperti:

u=n=>{for(s=[,1,1],r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j];return n>1?r:[1]}

Di sini saya membuang setidaknya 9 karakter karena pengembalian. Ini bisa dioptimalkan.

  • Cobalah untuk menghindari loop. Gunakan .mapatau .everyatau .somesebagai gantinya. Perhatikan bahwa jika Anda ingin mengubah array yang sama dengan yang Anda pemetaan, itu akan gagal.
  • Bungkus loop dalam fungsi panah penutupan, mengubah fungsi panah utama sebagai pernyataan tunggal.

Jadi yang di atas menjadi:

u=n=>(s=>{for(r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j]})([,1,1])|n>1?r:[1]

karakter yang dihapus: {}return

karakter yang ditambahkan: (){}>|

Perhatikan bagaimana saya memanggil metode penutupan, yang dengan benar mengisi variabel ndan kemudian karena metode penutupan tidak mengembalikan apa pun (yaitu mengembalikan undefined), saya bitwise atau mengembalikan array nsemua dalam satu pernyataan tunggal dari fungsi panah luaru

Koma dan titik koma

Hindari mereka apa pun,

Jika Anda mendeklarasikan variabel dalam satu lingkaran, atau seperti yang disebutkan di bagian sebelumnya, menggunakan ,pernyataan terpisah untuk memiliki fungsi tanda panah pernyataan tunggal, maka Anda dapat menggunakan beberapa trik yang cukup bagus untuk menghindari hal itu ,atau ;untuk mengurangi beberapa byte terakhir.

Pertimbangkan kode ini:

r=v=>Math.random()*100|0;n=r();m=r();D=v=>A(n-x)+A(m-y);d=0;do{g();l=d;d=D();....

Di sini, saya memanggil banyak metode untuk menginisialisasi banyak variabel. Setiap inisialisasi menggunakan a ,atau ;. Ini dapat ditulis ulang sebagai:

r=v=>Math.random()*100|0;n=r(m=r(d=0));D=v=>A(n-x)+A(m-y);do{d=D(l=d,g());....

Perhatikan bagaimana saya menggunakan fakta bahwa metode ini tidak mengganggu variabel yang diteruskan ke sana dan menggunakan fakta itu untuk mencukur 3 byte.

Lain-lain

.search dari pada .indexOf

Keduanya memberikan hasil yang sama, tetapi searchlebih pendek. Meskipun pencarian mengharapkan Ekspresi Reguler, jadi gunakan dengan bijak.

`String Template`

Ini sangat berguna ketika Anda harus membuat satu atau lebih bagian string berdasarkan kondisi tertentu.

Ambil contoh berikut untuk menampilkan quine di JS

(f=x=>alert("(f="+f+")()"))()

vs.

(f=x=>alert(`(f=${f})()`))()

Dalam string template, yang merupakan string di dalam dua backquotes (`), apa pun di dalam sebuah ${ }diperlakukan sebagai kode dan dievaluasi untuk memasukkan jawaban yang dihasilkan dalam string.

Saya akan memposting beberapa trik lagi nanti. Selamat bermain golf!


1
Pencarian lebih pendek, gunakan jika memungkinkan! tapi itu tidak sama dengan .indexOf. .search ingin regexp, bukan string. Coba'abc'.search('.')
edc65

@ edc65 Diperbarui!
Pengoptimal

Anda dapat memodifikasi array asli dengan metode instance. Yang kedua adalah indeks saat ini, dan yang ketiga adalah array yang di-iterasi.
Isiah Meadows

8
"Bergabung dengan situs seminggu yang lalu" - 21.4k perwakilan ...
GamrCorps

2
Selain itu .map, rekursi adalah teknik lain yang terkadang dapat membantu Anda mengubah forperulangan menjadi ekspresi.
Neil

20

Menggunakan steno properti

Properti singkatan memungkinkan Anda mengatur variabel ke nilai array:

a=r[0];b=r[1] // ES5
[a,b]=r       // ES6 - 6 bytes saved

Ini juga dapat digunakan seperti:

a=r[0],b=r[2] // ES5
[a,,b]=r      // ES6 - 5 bytes saved

Anda bahkan dapat menggunakan ini untuk membalikkan variabel:

c=a,a=b,b=c // ES5 - uses extra variable
[b,a]=[a,b] // ES6 - not shorter, but more flexible

Anda juga dapat menggunakan ini untuk mempersingkat slice()fungsi.

z = [1, 2, 3, 4, 5];

a=z.slice(1) // a = [2,3,4,5]; ES5
[,...a]=z    // a = [2,3,4,5]; ES6

Konversi basis

ES6 menyediakan cara yang jauh lebih pendek untuk mengonversi bentuk Basis-2 (biner) dan Basis-8 (oktal) menjadi desimal:

0b111110111 // == 503
0o767       // == 503

+dapat digunakan untuk mengkonversi string biner, oktal atau hex ke angka desimal. Anda dapat menggunakan 0b,, 0odan 0x, masing-masing untuk biner, oktal, dan hex .:

parseInt(v,2) // ES5
+('0b'+v)     // ES6 - 4 bytes saved; use '0o' for octal and '0x' for hex
'0b'+v-0      // Shorter, but may not work in all cases
              // You can adapt this your case for better results

Jika Anda menggunakan ini> 7 kali, maka akan lebih pendek untuk menggunakan parseIntdan menamainya:

(p=parseInt)(v,2)

Sekarang pdapat digunakan untuk parseInt, menghemat banyak byte dalam jangka panjang.


Trik konversi basis bagus, tetapi lebih mungkin bahwa jumlah konversi akan dalam bentuk variabel, bukan literal, dalam hal ini, menjadi jauh lebih lama.
Pengoptimal

1
'0x'+v-0bahkan lebih pendek, tetapi mungkin tidak berfungsi dengan baik dalam beberapa skenario.
ETHproduksi

1
Omong-omong, 0767(ES5) lebih pendek dari 0o767notasi (ES6).
Camilo Martin

@CamiloMartin 0767adalah ekstensi non-standar, dan itu secara eksplisit dilarang dalam mode ketat.
Oriol

1
@Oriol mode ketat adalah meme buruk. Itu tidak membantu kinerja, tidak benar-benar memaksa Anda untuk menulis kode yang baik, dan toh tidak akan pernah menjadi default. 0-tepat literal oktal tidak pergi ke mana-mana, dan ecmascript sebagai valid 0o.
Camilo Martin

19

Menggunakan templat string dengan fungsi

Ketika Anda memiliki fungsi dengan satu string sebagai argumen. Anda dapat menghilangkan ()jika Anda tidak memiliki ekspresi:

join`` // Works
join`foobar` // Works
join`${5}` // Doesn't work 

9
Diperingatkan, ini sebenarnya melewati sebuah array. fun`string` sama dengan fun(["string"]), tidak fun("string"). Ini bagus untuk fungsi yang dilemparkan ke string, seperti alert, tetapi untuk yang lain ini dapat menyebabkan masalah. Untuk informasi lebih lanjut, lihat artikel MDN
Cyoce

5
Referensi cepat: fun`foo${1}bar${2}bazsama dengan meneleponfun(["foo","bar","baz"],1,2)
Cyoce

14

Pemahaman Array (Firefox 30-57)

Catatan: pemahaman array tidak pernah distandarisasi, dan dibuat usang dengan Firefox 58. Gunakan dengan risiko Anda sendiri.


Awalnya, spesifikasi ECMAScript 7 berisi banyak fitur berbasis array baru. Meskipun sebagian besar tidak berhasil masuk ke versi final, Firefox mendukung (ed) kemungkinan yang terbesar dari fitur-fitur ini: sintaks baru yang bagus yang dapat menggantikan .filterdan .mapdengan for(a of b)sintaksis. Ini sebuah contoh:

b.filter(a=>/\s/.test(a)).map(a=>a.length)
[for(a of b)if(/\s/.test(a))a.length]

Seperti yang Anda lihat, kedua baris tidak jauh berbeda, selain yang kedua tidak mengandung fungsi kata kunci dan panah yang besar. Tetapi ini hanya bertanggung jawab atas pesanan .filter().map(); apa yang terjadi jika Anda memilikinya .map().filter()? Itu benar-benar tergantung pada situasi:

b.map(a=>a[0]).filter(a=>a<'['&&a>'@')
[for(a of b)if(a<'['&&a>'@')a[0]]

b.map(a=>c.indexOf(a)).filter(a=>a>-1)
[for(a of b)if((d=c.indexOf(a))>-1)d]

b.map(a=>a.toString(2)).filter(a=>/01/.test(a))
[for(a of b)if(/01/.test(c=a.toString(2)))c]

Atau bagaimana jika Anda ingin baik .map atau .filter ? Yah, biasanya ternyata kurang OK:

b.map(a=>a.toString(2))
[for(a of b)a.toString(2)]

b.filter(a=>a%3&&a%5)
[for(a of b)if(a%3&&a%5)a]

Jadi saran saya adalah menggunakan pemahaman array di mana pun Anda biasanya menggunakan .map dan .filter , tetapi tidak hanya satu atau yang lain.

Pemahaman String

Hal yang baik tentang comprehensions ES7 adalah bahwa, tidak seperti fungsi array-spesifik seperti .mapdan .filter, mereka dapat digunakan pada setiap objek iterable, bukan hanya array. Ini sangat berguna ketika berhadapan dengan string. Misalnya, jika Anda ingin menjalankan setiap karakter cdalam string melalui c.charCodeAt():

x=>[...x].map(c=>c.charCodeAt())
x=>[for(c of x)c.charCodeAt()]

Itu dua byte yang disimpan dalam skala yang cukup kecil. Dan bagaimana jika Anda ingin memfilter karakter tertentu dalam sebuah string? Misalnya, yang ini hanya menyimpan huruf kapital:

x=>[...x].filter(c=>c<'['&&c>'@')
x=>[for(c of x)if(c<'['&&c>'@')c]

Hmm, itu tidak lebih pendek. Tetapi jika kita menggabungkan keduanya:

x=>[...x].filter(c=>c<'['&&c>'@').map(c=>c.charCodeAt())
x=>[for(c of x)if(c<'['&&c>'@')c.charCodeAt()]

Wow, seluruh 10 byte disimpan!

Keuntungan lain dari pemahaman string adalah string hardcoded menyimpan byte tambahan, karena Anda dapat menghilangkan spasi setelah of:

x=>[...'[](){}<>'].map(c=>x.split(c).length-1)
x=>[for(c of'[](){}<>')x.split(c).length-1]

x=>[...'[](){}<>'].filter(c=>x.split(c).length>3)
x=>[for(c of'[](){}<>')if(x.split(c).length>3)c]

Pengindeksan

Pemahaman array membuatnya sedikit lebih sulit untuk mendapatkan indeks saat ini di string / array, tetapi dapat dilakukan:

a.map((x,i)=>x+i).filter ((x,i)=>~i%2)
[for(x of(i=0,a))if(++i%2)x+i-1]

Hal utama yang harus diperhatikan adalah memastikan indeks bertambah setiap kali, bukan hanya ketika suatu kondisi terpenuhi.

Pemahaman generator

Pemahaman generator pada dasarnya memiliki sintaks yang sama dengan pemahaman array; cukup ganti tanda kurung dengan tanda kurung:

x=>(for(c of x)if(c<'['&&c>'@')c.charCodeAt())

Ini membuat generator yang fungsinya hampir sama dengan array, tapi itu cerita untuk jawaban lain.

Ringkasan

Pada dasarnya, meskipun pemahaman biasanya lebih pendek daripada .map().filter()itu, semua berujung pada kekhasan situasi. Cara terbaik untuk mencobanya adalah sekaligus dan melihat mana yang lebih baik.

PS Jangan ragu untuk menyarankan kiat terkait pemahaman lain atau cara untuk meningkatkan jawaban ini!


Berikut trik untuk rentang yang akan menghemat beberapa karakter lagi:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
Anda dapat memotong 11 byte lagi untuk membuat rentang dari 0 hingga x:x=>[...Array(x).keys()]
Mwr247

Yang terakhir untuk pemahaman di sana: n=>[for(x of Array(n).keys())if(/1/.test(x))x](menghemat 7 byte)
Mwr247

@ Mwr247 Sebenarnya, saya bisa melihat sekarang bahwa rentang biasanya tidak sesingkat dengan pemahaman seperti dengan fitur ES6 bagus lainnya. Saya akan menambahkan bagian pada string sebagai gantinya, dan membiarkan Anda menangani rentang.
ETHproduk

Perlu dicatat bahwa Array Comprehensions telah ditinggalkan dan dihapus dari semua versi javascript terbaru. Lihat dokumen MDN tentang masalah ini.
Keefer Rourke

13

Ekspresi fungsi di ES6 menggunakan notasi panah, dan ini membantu banyak menghemat byte jika dibandingkan dengan versi ES5:

f=function(x,y){return x+y}
f=(x,y)=>x+y

Jika fungsi Anda hanya memiliki satu parameter, Anda dapat menghilangkan tanda kurung untuk menyimpan dua byte:

f=x=>x+1

Jika fungsi Anda tidak memiliki parameter sama sekali, mendeklarasikannya seolah-olah ia memiliki satu untuk menyimpan satu byte:

f=()=>"something"
f=x=>"something"

Hati-hati: Fungsi panah tidak persis sama dengan function () {}. Aturan thisuntuknya berbeda (dan IMO lebih baik). Lihat dokumen


2
Tetapi ketika Anda bermain golf, Anda biasanya tidak peduli thisdll.
Pengoptimal

1
Pada umumnya tidak, tetapi itu adalah peringatan, sesuatu yang Anda mungkin tidak pernah tahu kapan itu muncul. Ini juga lebih umum untuk lambdas untuk tidak memerlukan fungsi-lokal yang mengikat dalam produksi ini.
Isiah Meadows

Juga, jika Anda ingin mengambil semua argumen Anda, Anda dapat menggunakan fitur argumen "rest", misalnya, f=(...x)=>x akan memilikinya f(1,2,3) => [1,2,3].
Conor O'Brien

1
Berikut tip khusus untuk situs ini: jika Anda menjawab dengan fungsi yang mengambil bentuk (x,y)=>...Anda dapat menyimpan byte dengan currying dengan menggantinya denganx=>y=>...
Cyoce

12

Menggunakan evaluntuk fungsi panah dengan banyak pernyataan dan areturn

Salah satu trik konyol yang saya temukan ...

Bayangkan fungsi panah sederhana yang membutuhkan banyak pernyataan dan a return.

a=>{for(o="",i=0;i<a;i++)o+=i;return o}

Sebuah fungsi sederhana yang menerima parameter tunggal a, yang iterates atas semua bilangan bulat [0, a), dan mengaitkannya ke ujung string output o, yang dikembalikan. Misalnya, memanggil ini dengan 4sebagai parameter akan menghasilkan 0123.

Perhatikan bahwa fungsi panah ini harus dibungkus dengan kawat gigi {}, dan miliki return odi bagian akhir.

Upaya pertama ini berbobot 39 byte .

Tidak buruk, tetapi dengan menggunakan eval, kita dapat meningkatkan ini.

a=>eval('for(o="",i=0;i<a;i++)o+=i;o')

Fungsi ini menghapus kawat gigi dan pernyataan kembali dengan membungkus kode dalam evaldan hanya membuat pernyataan terakhir di evalevaluasi o. Ini menyebabkan evalkembali o, yang pada gilirannya menyebabkan fungsi kembali o, karena sekarang menjadi pernyataan tunggal.

Upaya yang ditingkatkan ini berbobot 38 byte , menghemat satu byte dari aslinya.

Tapi tunggu, masih ada lagi! Pernyataan valid mengembalikan apa pun pernyataan terakhir mereka dievaluasi. Dalam hal ini, lakukan o+=ievaluasi o, jadi kita tidak perlu ;o! (Terima kasih, edc65!)

a=>eval('for(o="",i=0;i<a;i++)o+=i')

Upaya terakhir ini hanya berbobot 36 byte - penghematan 3 byte dari yang asli!


Teknik ini dapat diperluas ke kasus umum di mana fungsi panah perlu mengembalikan nilai dan memiliki beberapa pernyataan (yang tidak dapat digabungkan dengan cara lain)

b=>{statement1;statement2;return v}

menjadi

b=>eval('statement1;statement2;v')

menghemat satu byte.

Jika statement2dievaluasi v, ini bisa

b=>eval('statement1;statement2')

menghemat total 3 byte.


1
Saya pikir, hanya menulis fungsi anonim mungkin lebih pendek
Downgoat

@vihan ya, kedua fungsi ini bisa dibuat anonim untuk menyimpan masing-masing 2 byte. Penghematan satu byte tetap berlaku.
jrich

1
Tetapi yang lebih baik: eval mengembalikan ekspresi terakhir yang dievaluasi, jadi Anda tidak perlu ;o- coba saja:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
Tapi string template!
Conor O'Brien

1
@ Cį“É“į“Ź€O'BŹ€ÉŖį“‡É“ Ingin menjelaskan bagaimana string templat akan bekerja di sini menggunakan fungsi contoh sebagai konteks?
WallyWest

10

Lebih suka string templat baris baru di atas "\ n"

Ini akan mulai menghasilkan bahkan pada satu baris karakter baru dalam kode Anda. Satu use case mungkin:

(16 byte)

array.join("\n")

(15 byte)

array.join(`
`)

Pembaruan: Anda bahkan dapat meninggalkan kawat gigi karena string templat yang ditandai (terima kasih, edc65!):

(13 byte)

array.join`
`

5
Tetapi lebih baik lagi, Anda dapat menghindari tanda kurung. Baca dokumen ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ā€¦ ) untuk mengetahui alasannya
edc65

Ah benar Terima kasih, saya telah menambahkannya.
Chiru

9

Mengisi Array - Nilai Statis & Rentang Dinamis

Saya awalnya meninggalkan ini sebagai komentar di bawah pemahaman, tetapi karena posting itu terutama difokuskan pada pemahaman, saya pikir itu akan baik untuk memberikan ini tempat itu sendiri.

ES6 memberi kami kemampuan untuk mengisi array dengan nilai-nilai statis tanpa menggunakan loop:

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=0;return a}

// ES6
x=>Array(x).fill(0)

Keduanya mengembalikan array dengan panjang x, diisi dengan nilai 0.

Namun, jika Anda ingin mengisi array dengan nilai dinamis (seperti rentang dari 0 ... x), hasilnya sedikit lebih lama (meskipun masih lebih pendek dari cara lama):

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=i;return a}

// ES6
x=>Array(x).fill().map((a,i)=>i)

Keduanya mengembalikan array dengan panjang x, dimulai dengan nilai 0 dan berakhir dengan x-1.

Alasan Anda membutuhkannya .fill()di sana adalah karena hanya menginisialisasi array tidak akan membiarkan Anda memetakannya. Artinya, melakukan x=>Array(x).map((a,i)=>i)akan mengembalikan array kosong. Anda juga dapat menyiasati kebutuhan untuk mengisi (dan karenanya membuatnya lebih pendek) dengan menggunakan operator spread seperti:

x=>[...Array(x)]

Dengan menggunakan operator dan .keys()fungsi spread , kini Anda dapat membuat kisaran 0 ... x pendek:

x=>[...Array(x).keys()]

Jika Anda ingin rentang khusus dari x ... y, atau rentang khusus sama sekali (seperti angka genap), Anda dapat menyingkirkan .keys()dan hanya menggunakan .map(), atau menggunakan .filter(), dengan operator spread:

// Custom range from x...y
(x,y)=>[...Array(y-x)].map(a=>x++)

// Even numbers (using map)
x=>[...Array(x/2)].map((a,i)=>i*2)

// Even numbers (using filter)
x=>[...Array(x).keys()].filter(a=>~a%2)

Berikut adalah saran untuk contoh kedua: x=>Array(x).fill(i=0).map(a=>i++)Juga, saya tidak yakin bahwa 0 in .fill(0)diperlukan. Sudahkah Anda mencobanya tanpa?
ETHproduk

@ ETHproductions Anda benar, saya lupa 0 tidak diperlukan di isi sebelum peta. Ini membuatnya 1 karakter lebih pendek dari yang Anda sarankan, jadi saya akan tetap seperti itu. Terima kasih!
Mwr247

Juga, untuk contoh terakhir, a=>a%2-1berfungsi dengan baik, seperti halnya a=>a%2<1.
ETHproduk

1
Trik baru yang saya pelajari: [...Array(x)]berfungsi dengan baik Array(x).fill(), dan 2 byte lebih pendek. x=>[...Array(x)].map((a,i)=>i)
Produk ETH

1
@yonatanmn Bagus sekali! Hanya komentar yang akan 1) 1/4contohnya akan lebih pendek ditulis [0,0,0,0], dan 2) fungsi yang diketikkan adalah implementasi spesifik, jadi tidak akan menghasilkan panjang yang dapat diandalkan ( Mapadalah 32 byte di Chrome, tetapi 36 byte di Firefox).
Mwr247

9

Mengembalikan Nilai dalam Fungsi Panah

Sudah menjadi rahasia umum bahwa jika satu pernyataan mengikuti deklarasi fungsi panah, itu mengembalikan hasil pernyataan itu:

a=>{return a+3}
a=>a+3

-7 byte

Jadi, jika memungkinkan, gabungkan beberapa pernyataan menjadi satu. Ini paling mudah dilakukan dengan mengelilingi pernyataan dengan tanda kurung dan memisahkannya dengan koma:

a=>{r=0;a.map(n=>r+=n);return r}
a=>(r=0,a.map(n=>r+=n),r)

-8 byte

Tetapi jika hanya ada dua pernyataan, biasanya mungkin (dan lebih pendek) untuk menggabungkannya dengan &&atau ||:

a=>{r=0;a.map(n=>r+=n);return r}

// - Use && because map always returns an array (true)
// - declaration of r moved into unused map argument to make it only 2 statements
a=>a.map(n=>r+=n,r=0)&&r

-9 byte

Akhirnya jika Anda menggunakan peta (atau yang serupa) dan perlu mengembalikan nomor dan Anda dapat menjamin peta tidak akan pernah mengembalikan array 1-panjang dengan nomor, Anda dapat mengembalikan nomor dengan |:

a=>{a=b=0;a.map(n=>(a+=n,b-=n));return a/b}

// - {} in map ensures it returns an array of undefined, so the | will make the returned
//   array cast from [ undefined, undefined, undefined ] to ",," to NaN to 0 and 0|n = n,
//   if the map returned [ 4 ] it would cast from [ 4 ] to "4" to 4 and make it 4|n
a=>a.map(n=>{a+=n,b-=n},a=b=0)|a/b

Dalam contoh terakhir itu, Anda juga perlu memastikan bahwa angka akan selalu berupa bilangan bulat.
ETHproduk

8

Retasan template-string acak

Fungsi ini riffles dua string (yaitu berubah "abc","de"menjadi "adbec"):

f=(x,y)=>String.raw({raw:x},...y)

Perhatikan bahwa ini hanya berfungsi bila xlebih lama dari y. Bagaimana cara kerjanya, Anda bertanya? String.rawdirancang untuk menjadi tag templat, seperti:

String.raw`x: ${x}\ny: ${y}\nx + y: ${x + y}`

Ini pada dasarnya panggilan String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), meskipun tidak sesederhana itu. Array template juga memiliki rawproperti khusus , yang pada dasarnya adalah salinan array, tetapi dengan string mentah. String.raw(x, ...args)pada dasarnya pengembalian x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...dan seterusnya sampai xkehabisan barang.

Jadi sekarang kita tahu cara String.rawkerjanya, kita bisa menggunakannya untuk keuntungan kita:

f=(x,y)=>String.raw({raw:x},...y)                   // f("abc", "de") => "adbec"
f=x=>String.raw({raw:x},...[...x].keys())           // f("abc") => "a0b1c"
f=(x,y)=>String.raw({raw:x},...[...x].fill(y))      // f("abc", " ") => "a b c"

Tentu saja, untuk yang terakhir, f=(x,y)=>x.split``.join(y)jauh lebih pendek, tetapi Anda mendapatkan idenya.

Berikut adalah beberapa fungsi riffling yang juga berfungsi jika xdan ymemiliki panjang yang sama:

f=(x,y)=>String.raw({raw:x.match(/.?/g)},...y)
f=(x,y)=>String.raw({raw:x},...y)+y.slice(-1)  // Only works if x.length == y.length

Anda dapat mempelajari lebih lanjut tentang String.raw MDN .


7

Cara bermain golf dengan rekursi

Rekursi, meskipun bukan opsi tercepat, seringkali merupakan yang terpendek. Secara umum, rekursi adalah yang terpendek jika solusi dapat disederhanakan menjadi solusi untuk bagian tantangan yang lebih kecil, terutama jika inputnya berupa angka atau string. Misalnya, jika f("abcd")dapat dihitung dari "a"dan f("bcd"), biasanya yang terbaik adalah menggunakan rekursi.

Ambil, misalnya, faktorial:

n=>[...Array(n).keys()].reduce((x,y)=>x*++y,1)
n=>[...Array(n)].reduce((x,_,i)=>x*++i,1)
n=>[...Array(n)].reduce(x=>x*n--,1)
n=>{for(t=1;n;)t*=n--;return t}
n=>eval("for(t=1;n;)t*=n--")
f=n=>n?n*f(n-1):1

Dalam contoh ini, rekursi jelas jauh lebih pendek daripada opsi lainnya.

Bagaimana dengan jumlah karakter:

s=>[...s].map(x=>t+=x.charCodeAt(),t=0)|t
s=>[...s].reduce((t,x)=>t+x.charCodeAt())
s=>[for(x of(t=0,s))t+=x.charCodeAt()]|t  // Firefox 30+ only
f=s=>s?s.charCodeAt()+f(s.slice(1)):0

Yang ini lebih sulit, tetapi kita dapat melihat bahwa ketika diimplementasikan dengan benar, rekursi menghemat 4 byte .map.

Sekarang mari kita lihat berbagai jenis rekursi:

Pra rekursi

Ini biasanya jenis rekursi terpendek. Input dibagi menjadi dua bagian adan b, dan fungsi menghitung sesuatu dengan adan f(b). Kembali ke contoh faktorial kami:

f=n=>n?n*f(n-1):1

Dalam hal ini, aadalah n , badalah n-1 , dan nilai yang dikembalikan adalah a*f(b).

Catatan penting: Semua fungsi rekursif harus memiliki cara untuk berhenti berulang ketika inputnya cukup kecil. Dalam fungsi faktorial, ini dikontrol dengan n? :1, yaitu jika inputnya 0 , kembalikan 1 tanpa menelepon flagi.

Pasca rekursi

Pasca rekursi mirip dengan pra rekursi, tetapi sedikit berbeda. Input dibagi menjadi dua bagian adan b, dan fungsi menghitung sesuatu dengan a, lalu memanggil f(b,a). Argumen kedua biasanya memiliki nilai default (yaitu f(a,b=1)).

Pra-rekursi baik ketika Anda perlu melakukan sesuatu yang istimewa dengan hasil akhir. Misalnya, jika Anda ingin faktorial dari angka plus 1:

f=(n,p=1)=>n?f(n-1,n*p):p+1

Namun demikian, post-tidak selalu lebih pendek dari menggunakan pra-rekursi dalam fungsi lain:

n=>(f=n=>n?n*f(n-1):1)(n)+1

Jadi kapan lebih pendek? Anda mungkin memperhatikan bahwa pasca rekursi dalam contoh ini membutuhkan tanda kurung di sekitar argumen fungsi, sedangkan pra-rekursi tidak. Secara umum, jika kedua solusi membutuhkan tanda kurung di sekitar argumen, pasca-rekursi sekitar 2 byte lebih pendek:

n=>!(g=([x,...a])=>a[0]?x-a.pop()+g(a):0)(n)
f=([x,...a],n=0)=>a[0]?f(a,x-a.pop()+n):!n

(program di sini diambil dari jawaban ini )

Cara menemukan solusi terpendek

Biasanya satu-satunya cara untuk menemukan metode terpendek adalah dengan mencoba semuanya. Ini termasuk:

  • Loop
  • .map(untuk string, baik [...s].mapatau s.replace; untuk angka, Anda dapat membuat rentang )
  • Array pemahaman
  • Pra-rekursi (kadang-kadang di dalam yang lain dari opsi ini)
  • Pasca rekursi

Dan ini hanyalah solusi yang paling umum; solusi terbaik mungkin merupakan kombinasi dari ini, atau bahkan sesuatu yang sama sekali berbeda . Cara terbaik untuk menemukan solusi terpendek adalah mencoba segalanya .


1
+1 untuk nilainya, dan saya ingin menambahkan +1 lainnya untuk zootopia
edc65

7

Cara yang lebih pendek untuk dilakukan .replace


Jika Anda ingin mengganti semua instance dari satu substring tepat dengan yang lain dalam string, cara yang jelas adalah:

f=s=>s.replace(/l/g,"y") // 24 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

Namun, Anda dapat melakukan 1 byte lebih pendek:

f=s=>s.split`l`.join`y`  // 23 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

Perhatikan bahwa ini tidak lagi lebih pendek jika Anda ingin menggunakan fitur regex apa pun selain gbendera. Namun, jika Anda mengganti semua instance variabel, biasanya jauh lebih pendek:

f=(s,c)=>s.replace(RegExp(c,"g"),"") // 36 bytes
f=(s,c)=>s.split(c).join``           // 26 bytes
f("Hello, World!","l") // -> "Heo, Word!"

Terkadang Anda ingin memetakan masing-masing char dalam sebuah string, mengganti masing-masing dengan yang lain. Saya sering menemukan diri saya melakukan ini:

f=s=>s.split``.map(x=>x+x).join`` // 33 bytes
f=s=>[...s].map(x=>x+x).join``    // 30 bytes
f("abc") // -> "aabbcc"

Namun, .replacehampir selalu lebih pendek:

f=s=>s.replace(/./g,x=>x+x)  // 27 bytes
f=s=>s.replace(/./g,"$&$&")  // Also works in this particular case

Sekarang, jika Anda ingin memetakan masing-masing karakter dalam string tetapi tidak peduli dengan string yang dihasilkan, .mapbiasanya lebih baik karena Anda dapat menyingkirkan .join``:

f=s=>s.replace(/./g,x=>t+=+x,t=0)&&t // 36 bytes
f=s=>[...s].map(x=>t+=+x,t=0)&&t     // 32 bytes
f("12345")  // -> 15

Untuk kasus terakhir, jika hanya karakter tertentu yang cocok dengan regex (seperti /\w/g) yang tertarik, maka menggunakan penggantian akan jauh lebih baik seperti dalam demo ini .
Shieru Asakoto

6

Menulis literal RegEx dengan eval

Konstruktor regex bisa sangat besar karena nama panjangnya. Alih-alih, tulis literal dengan eval dan backtick:

eval(`/<${i} [^>]+/g`)

Jika variabelnya isama dengan foo, ini akan menghasilkan:

/<foo [^>]+/g

Ini sama dengan:

new RegExp("<"+i+" [^>]+","g")

Anda juga dapat menggunakan String.rawuntuk menghindari keharusan berulang kali melarikan diri dari garis miring terbalik\

eval(String.raw`/\(?:\d{4})?\d{3}\d{3}\d{3}\d{3}\d{3}\d{3}\d{4}/g`)

Ini akan menampilkan:

/(?:\d{4})?\d{3}\d{3}\d{3}/g

Yang sama dengan:

RegExp("\\(?:\\d{4})?\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{4}","g")

Ingatlah!

String.rawmembutuhkan banyak byte dan kecuali Anda memiliki setidaknya sembilan backslash, String.rawakan lebih lama.


Anda tidak perlu newdi sana, jadi menggunakan konstruktor sebenarnya lebih pendek untuk contoh kedua
Pengoptimal

5

.forEachvs forloop

Selalu lebih suka .mapapa pun untuk loop. Mudah, penghematan instan.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • Total 8 byte untuk aslinya
  • 8 byte disimpan vs untuk-dari ( pengurangan 50% )
  • 22 byte disimpan vs C-style untuk loop ( pengurangan 73% )

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • Total 16 byte untuk aslinya
  • 2 byte disimpan vs untuk-dari ( pengurangan 11% )
  • 16 byte disimpan vs C-style untuk loop ( pengurangan 50% )

a.map((x,i)=>f(x,i,0))
for(i in a)f(a[i],i,0);
for(i=0;i<a.length;)f(a[i],i++,0);
  • Total 22 byte untuk aslinya
  • 1 byte disimpan vs untuk-dalam ( pengurangan 4% )
  • 11 byte disimpan vs C-style untuk loop ( pengurangan 33% )

a.map(x=>f(x)&g(x))
for(x of a)f(x),g(x);
for(i=0;i<a.length;)f(x=a[i++]),g(x);
  • Total 19 byte untuk aslinya
  • 2 byte disimpan vs untuk-dari ( pengurangan 10% )
  • 18 byte disimpan vs C-style untuk loop ( pengurangan 49% )

5

Menggunakan penghitung yang tidak diinisialisasi dalam rekursi

Catatan : Sebenarnya, ini tidak khusus untuk ES6. Akan lebih masuk akal untuk menggunakan dan menyalahgunakan rekursi dalam ES6, karena sifat fungsi panah yang ringkas.


Adalah lebih umum untuk menemukan fungsi rekursif yang menggunakan penghitung yang kawalnya disetel ke nol dan bertambah pada setiap iterasi:

f = (ā€¦, k=0) => [do a recursive call with f(ā€¦, k+1)]

Dalam keadaan tertentu, dimungkinkan untuk mengabaikan inisialisasi penghitung seperti itu dan ganti k+1dengan -~k:

f = (ā€¦, k) => [do a recursive call with f(ā€¦, -~k)]

Trik ini biasanya menghemat 2 byte .

Mengapa dan kapan itu bekerja?

Formula yang memungkinkannya adalah ~undefined === -1. Jadi, pada iterasi pertama, -~kakan dievaluasi 1. Pada iterasi berikutnya, -~kpada dasarnya setara dengan -(-k-1)yang sama k+1, setidaknya untuk bilangan bulat dalam kisaran [0 ... 2 31 -1].

Namun Anda harus memastikan bahwa memiliki k = undefinediterasi pertama tidak akan mengganggu perilaku fungsi. Anda harus selalu mengingat bahwa sebagian besar operasi aritmatika yang melibatkan undefinedakan menghasilkan NaN.

Contoh 1

Diberi bilangan bulat positif n, fungsi ini mencari bilangan bulat terkecil kyang tidak membagi n:

f=(n,k=0)=>n%k?k:f(n,k+1)   // 25 bytes

Ini dapat disingkat menjadi:

f=(n,k)=>n%k?k:f(n,-~k)     // 23 bytes

Ini bekerja karena n % undefinedadalah NaN, yang falsy. Itulah hasil yang diharapkan pada iterasi pertama.

[Tautan ke jawaban asli]

Contoh # 2

Diberikan bilangan bulat positif n, fungsi ini mencari bilangan bulat psedemikian rupa sehingga (3**p) - 1 == n:

f=(n,p=0,k=1)=>n<k?n>k-2&&p:f(n,p+1,k*3)  // 40 bytes

Ini dapat disingkat menjadi:

f=(n,p,k=1)=>n<k?n>k-2&&p:f(n,-~p,k*3)    // 38 bytes

Ini berfungsi karena ptidak digunakan sama sekali pada iterasi pertama ( n<kmenjadi salah).

[Tautan ke jawaban asli]


5

Fungsi ES6

Matematika

Math.cbrt(x)menyimpan karakter daripada Math.pow(x,1/3).

Math.cbrt(x)
Math.pow(x,1/3)

3 karakter disimpan

Math.hypot(...args)berguna ketika Anda membutuhkan akar kuadrat dari jumlah kuadrat dari argumen. Membuat kode ES5 untuk melakukannya jauh lebih sulit daripada menggunakan built-in.

Fungsi Math.trunc(x)tidak akan membantu, karena x|0lebih pendek. (Terima kasih, Mwr247!)

Ada banyak properti yang membutuhkan banyak kode untuk dilakukan di ES5, tetapi lebih mudah di ES6:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Menghitung ekuivalen hiperbolik fungsi trigonometri.
  • Math.clz32. Mungkin bisa dilakukan di ES5, tetapi sekarang lebih mudah. Menghitung angka nol di depan dalam representasi 32-bit angka.

Ada lebih banyak, jadi aku hanya akan daftar beberapa:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)empat kali lebih lama dari x|0.
Mwr247

@ mwr247: Oke, akan diperbarui.
ev3commander

Berikut ini ekivalen ES5 terpendek yang saya ketahui untuk beberapa fungsi ini: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 byte lebih lama; semakin panjang dengan lebih banyak argumen), Math.sign(a) => (a>0)-(a<0)(1 byte lebih pendek, tetapi membutuhkan tanda kurung di sekitarnya dalam beberapa kasus; mungkin tidak bekerja dengan NaN)
ETHproductions

@ ETHproductions Anda perlu array argumen untuk (penyelesaian es5) hipot. Dan apakah Anda yakin bahwa solusi untuk Math.sign bekerja untuk -0? (Seharusnya kembali -0)
ev3commander

1
@ ev3commander Ini hanya dimaksudkan sebagai pengganti in-line untuk setara ES6 masing-masing, sehingga mereka diperkecil untuk 99% penggunaan. Benar-benar menciptakan kembali fungsi-fungsi ini akan membutuhkan lebih banyak kode. Juga, saya tidak melihat alasan untuk perlu memiliki case khusus untuk -0, karena (AFAIK) tidak ada cara untuk mendapatkan -0 kecuali dengan menentukannya secara manual, dan secara praktis tidak digunakan untuk itu dalam kode-golf. Tetapi terima kasih telah menunjukkan hal-hal itu.
ETHproduk

5

Mengoptimalkan rentang konstan kecil untuk map()

Konteks

map()for[0 ..N-1]

for(i = 0; i < 10; i++) {
  do_something_with(i);
}

dapat diganti dengan:

[...Array(10).keys()].map(i => do_something_with(i))

atau lebih umum:

[...Array(10)].map((_, i) => do_something_with(i))

Array(N)N

[0 ..N-1]

saya

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ā‰¤ 6       | use a raw array of integers          | [0,1,2,3].map(i=>F(i))          | 2N+10
N = 7       | use either a raw array of integers   | [0,1,2,3,4,5,6].map(i=>F(i))    | 24
            | or a string if your code can operate | [...'0123456'].map(i=>F(i))     | 23
            | with characters rather than integers |                                 |
8 ā‰¤ N ā‰¤ 9   | use scientific notation 1e[N-1]      | [...1e7+''].map((_,i)=>F(i))    | 24
N = 10      | use scientific notation 1e9          | [...1e9+''].map((_,i)=>F(i))    | 24
            | or the ES7 expression 2**29+'4' if   | [...2**29+'4'].map(i=>F(i))     | 23
            | the order doesn't matter and your    |                                 |
            | code can operate with characters     |  (order: 5,3,6,8,7,0,9,1,2,4)   |
            | rather than integers                 |                                 |
11 ā‰¤ N ā‰¤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map((_,i)=>F(i))   | 25
N = 18      | use the fraction 1/3                 | [...1/3+''].map((_,i)=>F(i))    | 24
N = 19      | use the fraction 1/6                 | [...1/6+''].map((_,i)=>F(i))    | 24
20 ā‰¤ N ā‰¤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map((_,i)=>F(i))   | 25
N = 22      | use scientific notation -1e20        | [...-1e20+''].map((_,i)=>F(i))  | 26
23 ā‰¤ N ā‰¤ 99 | use Array(N)                         | [...Array(23)].map((_,i)=>F(i)) | 27

NB : Panjang kode panggilan balik F(i)tidak dihitung.

[1..9]

[1..9]

[...17**6+'8'].map(i=>F(i))  // order: 2,4,1,3,7,5,6,9,8; length: 23

Optimalisasi tanpa penghitung

N

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ā‰¤ 5       | use a raw array of integers          | [0,0,0,0].map(_=>F())           | 2N+10
6 ā‰¤ N ā‰¤ 10  | use scientific notation 1e[N-1]      | [...1e7+''].map(_=>F())         | 20
11 ā‰¤ N ā‰¤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map(_=>F())        | 21
N = 18      | use the fraction 1/3                 | [...1/3+''].map(_=>F())         | 20
N = 19      | use the fraction 1/6                 | [...1/6+''].map(_=>F())         | 20
20 ā‰¤ N ā‰¤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map(_=>F())        | 21
N = 22      | use scientific notation -1e20        | [...-1e20+''].map(_=>F())       | 22
23 ā‰¤ N ā‰¤ 99 | use Array(N)                         | [...Array(23)].map(_=>F())      | 23

NB : Panjang kode panggilan balik F()tidak dihitung.


Tidak 2**26seharusnya 2**29?
Shaggy

@Shaggy Heck. Tangkapan yang bagus!
Arnauld

Tidak ingin mengedit dalam diriku karena aku punya kode-buta! : D
Shaggy

Dengan menggunakan .keys(), Anda tidak perlu lambda:[...Array(10).keys()].map(do_something_with)
long-lazuli

@ long-lazuli Jika Anda tidak membutuhkan lambda dan hanya ingin jangkauan, maka Anda mungkin juga tidak perlu peta ...
Arnauld

4

Merusak penugasan tugas

ES6 memperkenalkan sintaks baru untuk penugasan yang merusak, yaitu memotong nilai menjadi beberapa bagian dan menempatkan setiap bagian ke variabel yang berbeda. Berikut ini beberapa contoh:

String dan array

a=s[0];b=s[1];       // 14 bytes
[a,b]=s;             //  8 bytes

a=s[0];s=s.slice(1); // 20 bytes
a=s.shift();         // 12 bytes, only works if s is an array
[a,...s]=s;          // 11 bytes, converts s to an array

Benda

a=o.asdf;b=o.bye;c=o.length; // 28 bytes
{asdf:a,bye:b,length:c}=o;   // 26 bytes

a=o.a;b=o.b;c=o.c; // 18 bytes
{a,b,c}=o;         // 10 bytes

Tugas-tugas ini juga dapat digunakan dalam parameter fungsi:

f=a=>a[0]+a[1]+a[2]
f=([a,b,c])=>a+b+c

f=b=>b[1]?b[0]+f(b.slice(1)):b[0]*2
f=b=>b[1]?b.shift()+f(b):b[0]*2
f=([a,...b])=>b[0]?a+f(b):a*2

4

Namun cara lain untuk menghindari return

Anda tahu Anda harus menggunakan eval untuk fungsi panah dengan banyak pernyataan dan pengembalian . Dalam beberapa kasus yang tidak biasa Anda dapat menyimpan lebih banyak menggunakan subfungsi bagian dalam.

Saya katakan tidak biasa karena

  1. Hasil yang dikembalikan tidak boleh berupa ekspresi terakhir yang dievaluasi dalam loop

  2. Harus ada (setidaknya) 2 inisialisasi yang berbeda sebelum loop

Dalam hal ini Anda dapat menggunakan subfungsi dalam tanpa kembali, memiliki salah satu dari nilai awal yang dilewatkan sebagai parameter.

Contoh Temukan kebalikan dari jumlah fungsi exp untuk nilai dalam rentang dari a ke b.

Jalan panjang - 55 byte

(a,b)=>{for(r=0,i=a;i<=b;i++)r+=Math.exp(i);return 1/r}

Dengan eval - 54 byte

(a,b)=>eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i);1/r")

Dengan fungsi bagian dalam - 53 byte

(a,b)=>(i=>{for(r=0;i<=b;i++)r+=Math.exp(i)})(a)||1/r

Perhatikan bahwa tanpa persyaratan batas kisaran yang lebih rendah a, saya dapat menggabungkan inisialisasi i dan r dan versi eval lebih pendek.


Dalam sampel Anda tidak perlu menyimpana
l4m2

@ l4m2 Saya tidak bisa mengerti maksud Anda, tolong ...
edc65

(i,b)=>{for(r=0;i<=b;i++)r+=Math.exp(i);return 1/r}
m

@ l4m2 eh benar, return a/rakan menjadi contoh yang lebih baik
edc65

1
eval masih lebih baik (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")dan dalam hal ini(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon

4

Menggunakan sintaks currying untuk fungsi diad dan rekursif

Fungsi diad

Setiap kali suatu fungsi mengambil dua argumen tepat tanpa nilai default, menggunakan sintaks currying menghemat satu byte.

Sebelum

f =
(a,b)=>a+b  // 10 bytes

Dipanggil dengan f(a,b)

Setelah

f =
a=>b=>a+b   // 9 bytes

Dipanggil dengan f(a)(b)

Catatan : Posting ini di Meta mengonfirmasi validitas sintaks ini.

Fungsi rekursif

Menggunakan sintaks currying juga dapat menyimpan beberapa byte ketika fungsi rekursif mengambil beberapa argumen tetapi hanya perlu memperbarui beberapa di antara setiap iterasi.

Contoh

Fungsi berikut menghitung jumlah semua bilangan bulat dalam rentang [a,b]:

f=(a,b)=>a>b?0:b+f(a,b-1)   // 25 bytes

Karena atetap tidak berubah selama seluruh proses, kita dapat menyimpan 3 byte dengan menggunakan:

f =                         // no need to include this assignment in the answer anymore
a=>F=b=>a>b?0:b+F(b-1)      // 22 bytes

Catatan : Seperti yang diperhatikan oleh Neil dalam komentar, fakta bahwa argumen tidak secara eksplisit diteruskan ke fungsi rekursif tidak berarti bahwa argumen tersebut harus dianggap tidak dapat diubah. Jika perlu, kita dapat memodifikasi adalam kode fungsi dengan a++, a--atau sintaksis yang serupa.


Contoh terakhir dapat ditulis sebagai a=>F=b=>a>b?0:a+++F(b), memodifikasi auntuk setiap panggilan rekursif. Ini tidak membantu dalam kasus itu tetapi mungkin menyimpan byte dalam kasus dengan lebih banyak argumen.
Neil

Heh, saya hanya berpikir tentang menulis tip untuk ini :-)
ETHproduksi

4

Fungsi pengujian utama

Fungsi 28-byte berikut ini mengembalikan trueuntuk bilangan prima dan falseuntuk non-bilangan prima:

f=(n,x=n)=>n%--x?f(n,x):x==1

Ini dapat dengan mudah dimodifikasi untuk menghitung hal-hal lain. Sebagai contoh, fungsi 39 byte ini menghitung jumlah bilangan prima kurang dari atau sama dengan angka:

f=(n,x=n)=>n?n%--x?f(n,x):!--x+f(n-1):0

Jika Anda sudah memiliki variabel nyang ingin diperiksa primality, fungsi primality dapat disederhanakan sedikit:

(f=x=>n%--x?f(x):x==1)(n)

Bagaimana itu bekerja

f = (         // Define a function f with these arguments:
  n,          //   n, the number to test;
  x = n       //   x, with a default value of n, the number to check for divisibility by.
) =>
  n % --x ?   //   If n is not divisible by x - 1,
  f(n, x)     //     return the result of f(n, x - 1).
              //   This loops down through all numbers between n and 0,
              //     stopping when it finds a number that divides n.
  : x == 1    //   Return x == 1; for primes only, 1 is the smallest number
              //     less than n that divides n.
              //   For 1, x == 0; for 0, x == -1.

Catatan: Ini akan gagal dengan kesalahan "terlalu banyak rekursi" ketika dipanggil dengan input yang cukup besar, seperti 12345. Anda bisa menyiasatinya dengan loop:

f=n=>eval('for(x=n;n%--x;);x==1')

1
Tetapi gagal dengan rekursi terlalu banyak untuk input hanya 12345
edc65

x==1mungkin bisa x<2untuk penghematan.
CalculatorFeline

@CalculatorFeline Terima kasih, tetapi kemudian gagal untuk 1atau 0(karena xakan 0atau -1, masing-masing)
ETHproduksi

Dapat bermanfaat dalam kasus-kasus tertentu. Juga, !~-xuntuk -0 byte.
CalculatorFeline

3

Array#concat() dan operator spread

Ini sangat tergantung pada situasi.


Menggabungkan banyak array.

Lebih suka fungsi concat kecuali kloning.

0 byte disimpan

a.concat(b)
[...a,...b]

3 byte terbuang

a.concat(b,c)
[...a,...b,...c]

3 byte disimpan

a.concat()
[...a]

6 byte disimpan

// Concatenate array of arrays
[].concat.apply([],l)
[].concat(...l)

Lebih suka menggunakan array yang sudah ada Array#concat().

Mudah 4 byte disimpan

[].concat(a,b)
a.concat(b)

3

Kembalikan hasil antara

Anda tahu bahwa menggunakan operator koma Anda dapat mengeksekusi urutan ekspresi yang mengembalikan nilai terakhir. Tetapi menyalahgunakan sintaks array literal, Anda dapat mengembalikan nilai perantara apa pun. Ini berguna dalam .map () misalnya.

// capitalize words
// f is a flag indicating if prev char is space
[...x].map(c=>(f?c=c.toUpperCase():0,f=c<'!',c),f=1).join('')

// shortened to ...
[...x].map(c=>[f?c.toUpperCase():c,f=c<'!'][0],f=1).join('')

3
Ingat, tentu saja, itu .join('')bisa.join``
Cyoce

3

Tetapkan default parameter fungsi

($,a,b,_)=>_!=undefined?'asdf':_ // before
($,a,b,_)=>_!=[]._?'asdf':_ // before, but a bit golfed
($,a,b,_='asdf')=>_ // after

Yang ini sangat berguna ...

Namun, pastikan untuk memahami bahwa sesuatu seperti _=>_||'asdf'lebih pendek ketika Anda hanya menyampaikan satu argumen (berguna) ke fungsi tersebut.


1
Saya perhatikan bahwa menggunakan OR _=>_||'asdf'biasanya lebih pendek dalam banyak kasus
Downgoat

@Downgoat saya perhatikan bahwa itu kembali "asdf"untuk input ""(string kosong).
ETHproduksi

2
Perhatikan bahwa default dievaluasi setiap kali argumen itu terjadi undefined, bahkan jika Anda secara eksplisit melewatkan nilai itu. Misalnya, [...Array(n)].map((a,b,c)=>b)selalu lewati undefineduntuk a, dan karena itu Anda dapat memberikan nilai default untuk itu (meskipun tidak dalam hal b).
Neil

3

Gunakan evalbukan kawat gigi untuk fungsi panah

Fungsi panah luar biasa. Mereka mengambil bentuk x=>y, di mana xargumen dan yadalah nilai balik. Namun, jika Anda perlu menggunakan struktur kontrol, seperti while, Anda harus meletakkan kawat gigi, misalnya =>{while(){};return}. Namun, kita bisa menyiasatinya; untungnya, evalfungsi tersebut mengambil string, mengevaluasi string itu sebagai kode JS, dan mengembalikan ekspresi yang terakhir dievaluasi . Sebagai contoh, bandingkan keduanya:

x=>{while(foo){bar};return baz} // before
x=>eval('while(foo){bar};baz')  // after
//                            ^

Kita dapat menggunakan ekstensi dari konsep ini untuk lebih mempersingkat kode kita: di mata eval, struktur kontrol juga mengembalikan ekspresi terakhir yang dievaluasi. Sebagai contoh:

x=>{while(foo)bar++;return bar} // before
x=>eval('while(foo)++bar')      // after
//                        ^^^^^

3

Operasi Logical Golf dalam ES6

"GLOE (S6)"

Logika Umum

Katakanlah Anda telah membuat pernyataan sdan t. Lihat apakah Anda dapat menggunakan salah satu dari penggantian berikut:

Traditional conjuction: s&&t
Equivalent conjuction: s*t OR s&t

Traditional disjunction: s||t
Equivalent disjunction: s+t OR s|t

(Ini mungkin tidak berfungsi jika pesanan salah; yaitu +dan *memiliki urutan prioritas lebih rendah daripada ||dan &&lakukan.)

Juga, berikut adalah beberapa ekspresi logis yang berguna:

  • Salah satau tbenar / XOR:s^t
  • sdan tmemiliki nilai kebenaran yang sama: !s^tataus==t

Logika array

Semua anggota akondisi memuaskan p:

a.every(p)                             // 10 bytes (11 bytes saved)
a.map(x=>c&=p(x),c=1)                  // 21 bytes (16 bytes saved)
for(i=0,c=1;i<a.length;c&=p(a[i++]));  // 37 bytes (hideously long)

Setidaknya satu anggota dengan akondisi memuaskan p:

a.some(p)                            // 9  bytes (13 bytes saved)
a.map(x=>c|=p(x),c=0)                // 21 bytes (14 bytes saved)
for(i=c=0;i<a.length;c|=p(a[i++]));  // 35 bytes (just please no)

Tidak ada anggota dari akondisi memuaskan p: !a.some(p).

Elemen eada dalam array a:

a.includes(e)                        // 13 bytes, standard built-in
~a.indexOf(e)                        // 13 bytes, "traditional" method
a.find(x=>e==x)                      // 15 bytes, find (ES6)
a.some(x=>x==e)                      // 15 bytes, some (ES5)
(a+"").search(e)                     // 16 bytes, buggy
a.filter(t=>t==e).length             // 24 bytes, no reason to use this
for(i=c=0;i<a.length;c+=e==a[i++]);  // 35 bytes, super-traditional

Elemen etidak tidak ada dalam array a:

!a.includes(e)
!~a.indexOf(e)
a.every(t=>t!=e)
!a.filter(t=>t==e).length
for(i=0,c=1;i<a.length;c*=e!=a[i++]);

Saya biasanya menggunakan &&dan ||sebagai x?y:xdan x?x:y, masing-masing. Tapi saya bisa melihat bagaimana ini akan berguna dalam program yang lebih berbasis logika. Satu masalah +adalah eg 3dan -3keduanya benar, tetapi 3+-3tidak.
ETHproduk

@ ETHproductions Ah, Anda benar; itu adalah kasus tepi. -bisa juga bekerja, jika s != t.
Conor O'Brien

a.filter(t=>t==e).length==a.lengthsalah. Seharusnya!a.filter(t=>t==e).length
ETHproduk

@ ETHproduk benar Anda!
Conor O'Brien

3

Mempersingkat pemanggilan fungsi berulang

Jika Anda telah berulang kali memanggil ke suatu fungsi dengan nama panjang, seperti manipulasi kanvas:

c.lineTo(0,100);c.lineTo(100,100);c.lineTo(100,0);c.lineto(0,0);c.stroke()

Cara tradisional untuk mempersingkatnya adalah dengan menggunakan nama fungsi:

c[l='lineTo'](0,100);c[l](100,100);c[l](100,0);c[l](0,0);c.stroke()

Jika Anda memiliki cukup panggilan, cara yang lebih baik adalah membuat fungsi yang berfungsi untuk Anda:

l=(x,y)=>c.lineTo(x,y);l(0,100);l(100,100);l(100,0);l(0,0);c.stroke()

Jika sebagian besar panggilan fungsi dirantai, Anda dapat membuat fungsi kembali dengan sendirinya, memungkinkan Anda untuk memotong dua byte dari setiap panggilan berikutnya:

l=(x,y)=>c.lineTo(x,y)||l;l(0,100)(100,100)(100,0)(0,0);c.stroke()

Contoh penggunaan: 1 , 2


1
Anda dapat mempersingkat dengan operator ikat :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@Downgoat Terima kasih, browser apa yang mendukungnya? (Juga, dari apa yang saya lihat akan kesalahan pada panggilan kedua, karena c.lineTotidak secara alami kembali sendiri)
ETHproduk

Anda harus menggosoknya melalui babel karena ini merupakan fitur ES7
Downgoat

3

Mengikat operator ::

Operator bind dapat digunakan untuk membantu mempersingkat byte dari fungsi yang berulang:

(x='abc'.search(a))+x.search(b) // Before
(x=::'abc'.search)(a)+x(b)      // 5 bytes saved

Selain itu jika Anda ingin menggunakan fungsi dengan yang berbeda thismisalnya:

s[r='replace'](/a/g,'b')+s[r](/c/g,'d') // Before
(r=s.replace)(/a/g,'b')+s::r(/c/g,'d')  // 1 byte saved

3

Menghindari koma saat menyimpan banyak data

Jika Anda memiliki banyak data (yaitu indeks, karakter, ...) yang harus Anda simpan dalam array, Anda mungkin lebih baik meninggalkan semua koma. Ini berfungsi baik jika setiap bagian data memiliki panjang string yang sama, 1 jelas menjadi optimal.

43 Bytes (baseline)

a=[[3,7,6,1,8,9,4,5,2],[5,4,3,2,7,6,5,4,3]]

34 Bytes (tanpa koma)

a=[[..."376189452"],[..."543276543"]]

Jika Anda ingin mengubah akses array Anda, Anda mungkin mengurangi ini lebih jauh, menyimpan nilai yang sama seperti:

27 Bytes (data yang sama, hanya mengubah akses array)

a=[..."376189452543276543"]

Mengapa hanya blok terakhir yang disorot?
CalculatorFeline

@CalculatorFeline Terima kasih, sudah diperbaiki.
Chiru
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.