Suggerimenti per giocare a golf in ECMAScript 6 e versioni successive


88

Questo è simile ad altri "Suggerimenti per giocare a golf in <...>" ma si rivolge in modo specifico alle nuove funzionalità di JavaScript introdotte in ECMAScript 6 e versioni successive.

JavaScript è un linguaggio di per sé molto verboso, function(){}, .forEach(), la conversione di stringa in array, array come oggetto di array, ecc, ecc sono bloats super e non è sano per il golf.

ES6 +, d'altra parte, ha alcune funzioni super pratiche e un ingombro ridotto. x=>y, [...x]ecc. sono solo alcuni degli esempi.

Per favore pubblica alcuni trucchi che possono aiutarti a radere quei pochi byte extra dal tuo codice.

NOTA: i trucchi per ES5 sono già disponibili in Suggerimenti per giocare a golf in JavaScript ; le risposte a questa discussione dovrebbero concentrarsi sui trucchi disponibili solo in ES6 e in altre versioni future di ES.

Tuttavia, questa discussione è anche per gli utenti che attualmente giocano a golf utilizzando le funzionalità ES5. Le risposte possono anche contenere suggerimenti per aiutarli a comprendere e mappare le funzionalità ES6 al loro stile di codifica ES5.

Risposte:


42

Operatore di diffusione ...

L'operatore spread trasforma un valore di array in un elenco separato da virgole.

Caso d'uso 1:

Utilizzare direttamente un array in cui una funzione prevede un elenco

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

Caso d'uso 2:

Crea un array letterale da un iterabile (in genere una stringa)

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

Caso d'uso 3:

Dichiarare un numero variabile di argomenti per una funzione

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

Vedi mozilla doc


3
Ora ho un downvote qui. Ovviamente qualcuno ha notato qualcosa di terribilmente sbagliato in questo suggerimento, essendo troppo timido per lasciare un commento e spiegare cosa ...
edc65

Sembra a posto. Forse era la mancanza di punti e virgola? ;) (a proposito, puoi anche usarlo come parametri di riposo, come splats in Ruby)
gcampbell

Potresti aggiungere che ha anche un caso d'uso nelle firme delle funzioni :)
Felix Dombek,

Misclick non voleva sottovalutare
Stan Strum

@StanStrum succede. Farò un piccolo aggiornamento a questo post in modo da poter eventualmente cambiare il tuo voto (o l'hai già fatto?)
edc65

21

Trucchi appresi qui da quando mi sono iscritto

Il mio linguaggio di programmazione principale è JS e principalmente ES6. Da quando sono entrato in questo sito una settimana fa, ho imparato molti trucchi utili dagli altri membri. Sto combinando alcuni di quelli qui. Tutti i crediti per la comunità.

Funzioni e loop della freccia

Sappiamo tutti che le funzioni delle frecce salvano molti byte

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

Ma devi tenere a mente alcune cose

  • Prova a club più istruzioni usando ,ie (a=b,a.map(d))- Qui, il valore che viene restituito è l'ultima espressionea.map(d)
  • se la tua do somethingparte è più di un'istruzione, devi aggiungere le {}parentesi circostanti .
  • Se ci sono {}parentesi circostanti , è necessario aggiungere un'istruzione esplicita di ritorno.

Quanto sopra vale molte volte quando si hanno dei loop coinvolti. Quindi qualcosa del tipo:

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]}

Qui sto sprecando almeno 9 caratteri a causa del ritorno. Questo può essere ottimizzato.

  • Cerca di evitare loop. Usa .mapo .everyo .someinvece. Si noti che se si desidera modificare lo stesso array su cui si sta mappando, non funzionerà.
  • Avvolgi il loop in una funzione freccia di chiusura, convertendo la funzione freccia principale in una singola istruzione.

Quindi quanto sopra diventa:

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]

personaggi rimossi: {}return

personaggi aggiunti: (){}>|

Nota come chiamo il metodo di chiusura, che popola correttamente la variabile ne quindi poiché il metodo di chiusura non restituisce nulla (ovvero restituisce undefined), io bit per bit o esso e restituisco l'array nin un'unica istruzione della funzione freccia esternau

Virgole e punti e virgola

Evitali come mai,

Se stai dichiarando variabili in un ciclo, o come menzionato nella sezione precedente, usando ,istruzioni separate per avere funzioni di freccia di istruzioni singole, puoi usare alcuni trucchi piuttosto ingegnosi per evitarli ,o ;radere quegli ultimi byte.

Considera questo codice:

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();....

Qui sto chiamando molti metodi per inizializzare molte variabili. Ogni inizializzazione utilizza un ,o ;. Questo può essere riscritto come:

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());....

Nota come uso il fatto che il metodo non disturba la variabile passata e usa quel fatto per radere 3 byte.

Varie

.search invece di .indexOf

Entrambi danno lo stesso risultato, ma searchè più breve. Sebbene la ricerca preveda un'espressione regolare, usala con saggezza.

"Stringhe modello"

Questi sono molto utili quando devi concatellare una o più parti di stringa in base a determinate condizioni.

Prendi l'esempio seguente per produrre un quine in JS

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

vs.

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

In una stringa di modello, che è una stringa all'interno di due backquotes (`), tutto all'interno di a ${ }viene trattato come un codice e valutato per inserire la risposta risultante nella stringa.

Pubblicherò qualche altro trucco più tardi. Buon golf!


1
.search è più breve, usalo quando possibile! ma non è lo stesso di .indexOf. .search vuole una regexp, non una stringa. Prova'abc'.search('.')
edc65,

@ edc65 Aggiornato!
Ottimizzatore

È possibile modificare l'array originale con i metodi di istanza. Il secondo è l'indice corrente e il terzo è l'array che viene iterato.
Isiah Meadows il

8
"Si è unito al sito una settimana fa" - 21.4k rep ...
GamrCorps il

2
Inoltre .map, la ricorsione è un'altra tecnica che a volte può aiutarti a trasformare un forciclo in un'espressione.
Neil,

20

Usando le scorciatoie di proprietà

Le scorciatoie di proprietà consentono di impostare le variabili sui valori di una matrice:

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

Questo può anche essere usato come:

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

Puoi persino usarlo per invertire le variabili:

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

Puoi anche usarlo per abbreviare le slice()funzioni.

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

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

Conversioni di base

ES6 fornisce un modo molto più breve per convertire la forma Base-2 (binaria) e Base-8 (ottale) in decimale:

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

+può essere utilizzato per convertire una stringa binaria, ottale o esadecimale in un numero decimale. È possibile utilizzare 0b, 0oe 0x, per binario, ottale, esadecimale e, rispettivamente, .:

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

Se lo stai usando> 7 volte, sarà più breve usarlo parseInte rinominarlo:

(p=parseInt)(v,2)

Ora ppuò essere utilizzato per parseInt, risparmiando molti byte nel lungo periodo.


Il trucco di conversione di base è carino, ma è più probabile che il numero di conversione sia nella forma di una variabile anziché in un valore letterale, nel qual caso diventa molto più lungo.
Ottimizzatore

1
'0x'+v-0è ancora più breve, ma potrebbe non funzionare anche in alcuni scenari.
ETHproductions

1
A proposito, 0767(ES5) è più breve della 0o767notazione (ES6).
Camilo Martin,

@CamiloMartin 0767è un'estensione non standard ed è esplicitamente vietato in modalità rigorosa.
Oriol,

1
La modalità rigorosa di @Oriol era un cattivo meme. Non ha aiutato le prestazioni, non ti ha costretto a scrivere un buon codice e non sarebbe mai diventato il valore predefinito comunque. 0i letterali ottali prefissati non vanno da nessuna parte e sono ecmascript validi come 0o.
Camilo Martin,

19

Utilizzo di modelli di stringa con funzioni

Quando hai una funzione con una stringa come argomento. Puoi omettere il ()se non hai espressioni:

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

9
Attenzione, questo in realtà passa un array. fun`string` è lo stesso di fun(["string"])no fun("string"). Questo va bene per funzioni che eseguono il cast su stringa, ad esempio alert, ma per altri ciò potrebbe causare problemi. Per ulteriori informazioni, consultare l'articolo MDN
Cyoce,

5
Cosa di riferimento rapido: fun`foo${1}bar${2}bazequivale a chiamarefun(["foo","bar","baz"],1,2)
Cyoce l'

14

Comprensioni dell'array (Firefox 30-57)

Nota: le comprensioni dell'array non sono mai state standardizzate e sono state rese obsolete con Firefox 58. Utilizzate a vostro rischio e pericolo.


Inizialmente, le specifiche ECMAScript 7 contenevano una serie di nuove funzionalità basate su array. Sebbene la maggior parte di questi non sia arrivata alla versione finalizzata, il supporto di Firefox (ed) è probabilmente la più grande di queste funzionalità: una nuova sintassi elegante che può sostituire .filtere .mapcon la for(a of b)sintassi. Ecco un esempio:

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

Come puoi vedere, le due linee non sono poi così diverse, a parte la seconda che non contiene le parole chiave ingombranti e le funzioni delle frecce. Ma questo spiega solo l'ordine .filter().map(); cosa succede se .map().filter()invece hai ? Dipende molto dalla situazione:

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]

O se si vuole uno .map o .filter ? Bene, di solito risulta meno 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]

Quindi il mio consiglio è di usare le comprensioni di array ovunque tu usi .map e .filter , ma non solo l'una o l'altra.

Comprensioni delle stringhe

Una cosa bella delle comprensioni di ES7 è che, diversamente dalle funzioni specifiche dell'array come .mape .filter, possono essere utilizzate su qualsiasi oggetto iterabile, non solo sugli array. Ciò è particolarmente utile quando si ha a che fare con le stringhe. Ad esempio, se si desidera eseguire ciascun carattere cin una stringa tramite c.charCodeAt():

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

Sono due byte salvati su una scala abbastanza piccola. E se si desidera filtrare determinati caratteri in una stringa? Ad esempio, questo conserva solo lettere maiuscole:

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

Hmm, non è più breve. Ma se combiniamo i due:

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

Caspita, salvati 10 byte interi!

Un altro vantaggio della comprensione delle stringhe è che le stringhe codificate salvano un byte in più, poiché è possibile omettere lo spazio dopo 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]

indicizzazione

Le comprensioni dell'array rendono un po 'più difficile ottenere l'indice corrente nella stringa / matrice, ma si può fare:

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

La cosa principale da fare attenzione è assicurarsi che l'indice venga incrementato ogni volta, non solo quando viene soddisfatta una condizione.

Comprensioni del generatore

Le comprensioni del generatore hanno sostanzialmente la stessa sintassi delle comprensioni dell'array; basta sostituire le parentesi con parentesi:

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

Questo crea un generatore che funziona più o meno allo stesso modo di un array, ma questa è una storia per un'altra risposta.

Sommario

Fondamentalmente, sebbene le comprensioni siano generalmente più brevi di .map().filter(), tutto dipende dai dettagli della situazione. È meglio provarlo in entrambi i modi e vedere quale funziona meglio.

PS Sentiti libero di suggerire un altro suggerimento relativo alla comprensione o un modo per migliorare questa risposta!


Ecco un trucco per le gamme che salverà un altro paio di personaggi:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
Puoi tagliare altri 11 byte per fare un intervallo da 0 a x:x=>[...Array(x).keys()]
Mwr247

L'ultimo per la comprensione lì: n=>[for(x of Array(n).keys())if(/1/.test(x))x](salva 7 byte)
Mwr247

@ Mwr247 In realtà, ora posso vedere che le gamme di solito non sono così brevi con la comprensione come con altre belle funzionalità ES6. Aggiungerò invece una sezione sulle stringhe e ti consentirò di gestire gli intervalli.
ETHproductions

Vale la pena notare che le comprensioni dell'array sono state deprecate e rimosse da tutte le versioni recenti di javascript. Vedi i documenti MDN sull'argomento.
Keefer Rourke,

13

Le espressioni di funzione in ES6 usano la notazione a freccia e aiuta molto a risparmiare byte rispetto alla versione ES5:

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

Se la tua funzione ha un solo parametro, puoi omettere le parentesi per salvare due byte:

f=x=>x+1

Se la tua funzione non ha alcun parametro, dichiarala come se ne avesse una per salvare un byte:

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

Attenzione: le funzioni delle frecce non sono esattamente le stesse di function () {}. Le regole per thissono diverse (e meglio IMO). Vedi documenti


2
Ma quando giochi a golf, di solito non ti interessa thisecc.
Ottimizzatore,

1
Generalmente no, ma è un avvertimento, uno che potresti non sapere mai quando si presenta. È anche più comune che lambda non abbia bisogno di una funzione locale questo legame nella produzione.
Isiah Meadows il

Inoltre, se si vuole prendere tutti i tuoi argomenti, è possibile utilizzare la funzione argomento "riposo", per esempio, f=(...x)=>x avrebbe quella f(1,2,3) => [1,2,3].
Conor O'Brien,

1
Ecco un suggerimento specifico per questo sito: se stai rispondendo con una funzione che assume la forma (x,y)=>...puoi salvare un byte con il curry sostituendolo conx=>y=>...
Cyoce

12

Utilizzo evalper le funzioni freccia con più istruzioni e areturn

Uno dei trucchi più ridicoli che mi sono imbattuto in ...

Immagina una semplice funzione freccia che richiede più istruzioni e a return.

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

Una semplice funzione che accetta un singolo parametro a, che scorre su tutti i numeri interi in [0, a)e li collega alla fine della stringa di output o, che viene restituita. Ad esempio, chiamando questo con 4come il parametro produrrebbe 0123.

Nota che questa funzione freccia doveva essere racchiusa tra parentesi graffe {}e avere un return oalla fine.

Questo primo tentativo pesa 39 byte .

Non male, ma utilizzando eval, possiamo migliorare questo.

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

Questa funzione ha rimosso le parentesi graffe e l'istruzione return restituendo il codice in un evale semplicemente facendo l'ultima istruzione nella evalvalutazione a o. Ciò causa il evalritorno o, che a sua volta provoca il ritorno della funzione o, poiché ora è una singola istruzione.

Questo tentativo migliorato pesa 38 byte , salvando un byte dall'originale.

Ma aspetta, c'è di più! Le dichiarazioni eval restituiscono qualunque sia la loro ultima dichiarazione valutata. In questo caso, o+=ivaluta o, quindi non abbiamo bisogno del ;o! (Grazie, edc65!)

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

Questo tentativo finale pesa solo 36 byte , un risparmio di 3 byte rispetto all'originale!


Questa tecnica potrebbe essere estesa a qualsiasi caso generale in cui una funzione freccia deve restituire un valore e avere più istruzioni (che non possono essere combinate con altri mezzi)

b=>{statement1;statement2;return v}

diventa

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

salvando un byte.

Se lo statement2valuta v, questo può essere

b=>eval('statement1;statement2')

salvando un totale di 3 byte.


1
Penso che solo scrivere una funzione anonima potrebbe essere ancora più breve
Downgoat,

@vihan sì, entrambe queste funzioni potrebbero essere rese anonime per salvare 2 byte ciascuna. Il risparmio di un byte è ancora valido.
jrich

1
Ma ancora meglio: eval restituisce l'ultima espressione valutata, quindi non è necessario ;o- provalo:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
Ma stringhe modello!
Conor O'Brien,

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ Vuoi spiegare come funzionerebbero le stringhe di template usando la funzione di esempio come contesto?
WallyWest,

10

Preferisci nuove stringhe di modello su "\ n"

Questo inizierà a pagare anche con un singolo carattere di nuova riga nel tuo codice. Un caso d'uso potrebbe essere:

(16 byte)

array.join("\n")

(15 byte)

array.join(`
`)

Aggiornamento: puoi anche lasciare le parentesi graffe a causa delle stringhe del modello con tag (grazie, edc65!):

(13 byte)

array.join`
`

5
Ma ancora meglio, puoi evitare la parentesi. Leggi i documenti ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) per vedere perché
edc65

Ah giusto. Grazie, l'ho aggiunto.
Chiru,

9

Array di riempimento: valori statici e intervalli dinamici

Inizialmente ho lasciato questi come commenti sotto comprensione, ma dato che quel post era principalmente incentrato sulle comprensioni, ho pensato che sarebbe stato bello dare a questo posto il proprio posto.

ES6 ci ha dato la possibilità di riempire le matrici con valori statici senza l'uso di loop:

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

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

Entrambi restituiscono un array di lunghezza x, riempito con il valore 0.

Se si desidera riempire le matrici con valori dinamici (come un intervallo compreso tra 0 ... x), il risultato è un po 'più lungo (sebbene sia ancora più breve del vecchio modo):

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

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

Entrambi restituiscono un array di lunghezza x, che inizia con il valore 0 e termina con x-1.

Il motivo per cui è necessario l'interno .fill()è perché l'inizializzazione di un array non consente di mapparlo. Vale a dire, fare x=>Array(x).map((a,i)=>i)restituirà un array vuoto. Puoi anche ovviare alla necessità di riempimento (e quindi renderlo ancora più breve) utilizzando l'operatore di diffusione in questo modo:

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

Usando l'operatore e la .keys()funzione spread , ora puoi creare un breve intervallo 0 ... x:

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

Se vuoi un intervallo personalizzato da x ... y, o un intervallo specializzato del tutto (come numeri pari), puoi sbarazzarti di .keys()e semplicemente usare .map(), o usare .filter(), con l'operatore 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)

Ecco un suggerimento per il secondo esempio: x=>Array(x).fill(i=0).map(a=>i++)Inoltre, non sono sicuro che lo 0 in .fill(0)sia necessario. L'hai provato senza?
ETHproductions

@ETHproductions Hai ragione, ho dimenticato che lo 0 non è necessario nel riempimento prima della mappa. Questo rende però 1 personaggio più corto di quello suggerito, quindi lo terrò così. Grazie!
Mwr247,

Inoltre, per l'ultimo esempio, a=>a%2-1funziona bene, così come a=>a%2<1.
ETHproductions

1
Ho imparato un nuovo trucco: [...Array(x)]funziona altrettanto bene Array(x).fill(), ed è più corto di 2 byte. x=>[...Array(x)].map((a,i)=>i)
ETHproductions

1
@yonatanmn Molto bello! Solo i commenti sarebbero 1) l' 1/4esempio sarebbe più breve [0,0,0,0], e 2) le funzioni con stringhe sono specifiche dell'implementazione, quindi non restituiranno una lunghezza affidabile ( Mapè 32 byte in Chrome, ma 36 byte in Firefox).
Mwr247,

9

Restituzione di valori nelle funzioni freccia

È risaputo che se una singola istruzione segue la dichiarazione della funzione freccia, restituisce il risultato di tale istruzione:

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

-7 byte

Quindi, quando possibile, combina più istruzioni in una. Questo è più facile facendo circondando le dichiarazioni tra parentesi e separandole con virgole:

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

-8 byte

Ma se ci sono solo due affermazioni, di solito è possibile (e più breve) combinarle con &&o ||:

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

Infine, se stai utilizzando la mappa (o simile) e devi restituire un numero e puoi garantire che la mappa non restituirà mai un array di 1 lunghezza con un numero, puoi restituire il numero con |:

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

In quest'ultimo esempio, devi anche essere sicuro che il numero sarà sempre un numero intero.
ETHproductions

8

Hack casuali di stringhe di modelli

Questa funzione sfoglia due stringhe (cioè si trasforma "abc","de"in "adbec"):

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

Nota che funziona solo quando xè più lungo di y. Come funziona, chiedi? String.rawè progettato per essere un tag modello, in questo modo:

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

Questo in pratica chiama String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), anche se non è così semplice. L'array modello ha anche una rawproprietà speciale , che è fondamentalmente una copia dell'array, ma con le stringhe non elaborate. String.raw(x, ...args)sostanzialmente ritorna x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...e così via fino a quando non si xesauriscono gli articoli.

Quindi ora che sappiamo come String.rawfunziona, possiamo usarlo a nostro vantaggio:

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"

Certo, per quest'ultimo, f=(x,y)=>x.split``.join(y)è molto più breve, ma hai l'idea.

Ecco un paio di funzioni di riffling che funzionano anche se xe ysono di uguale lunghezza:

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

Puoi saperne di più String.raw su MDN .


7

Come giocare a golf con la ricorsione

La ricorsione, sebbene non sia l'opzione più veloce, è spesso la più breve. In genere, la ricorsione è più breve se la soluzione può essere semplificata per una parte più piccola della sfida, soprattutto se l'input è un numero o una stringa. Ad esempio, se f("abcd")può essere calcolato da "a"e f("bcd"), di solito è meglio usare la ricorsione.

Prendi, ad esempio, fattoriale:

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

In questo esempio, la ricorsione è ovviamente molto più breve di qualsiasi altra opzione.

Che ne dici di somma di codici di carattere:

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

Questo è più complicato, ma possiamo vedere che se implementato correttamente, la ricorsione risparmia 4 byte .map.

Ora diamo un'occhiata ai diversi tipi di ricorsione:

Pre-ricorsione

Questo è di solito il tipo più breve di ricorsione. L'input è diviso in due parti ae b, e la funzione calcola qualcosa con ae f(b). Tornando al nostro esempio fattoriale:

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

In questo caso, aè n , bè n-1 e il valore restituito è a*f(b).

Nota importante: tutte le funzioni ricorsive devono avere un modo per interrompere la ricorrenza quando l'ingresso è abbastanza piccolo. Nella funzione fattoriale, questo è controllato con n? :1, ovvero se l'ingresso è 0 , restituisce 1 senza richiamare fnuovamente.

Post-ricorsione

La post-ricorsione è simile alla pre-ricorsione, ma leggermente diversa. L'input è diviso in due parti ae b, e la funzione calcola qualcosa con a, quindi chiama f(b,a). Il secondo argomento di solito ha un valore predefinito (cioè f(a,b=1)).

La pre-ricorsione è buona quando devi fare qualcosa di speciale con il risultato finale. Ad esempio, se si desidera il fattoriale di un numero più 1:

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

Anche allora, tuttavia, post- non è sempre più breve dell'uso della pre-ricorsione all'interno di un'altra funzione:

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

Quindi quando è più corto? Si può notare che in questo esempio la post-ricorsione richiede parentesi attorno agli argomenti della funzione, mentre la pre-ricorsione non lo è. In generale, se entrambe le soluzioni richiedono parentesi attorno agli argomenti, la post-ricorsione è più corta di circa 2 byte:

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

(programmi qui presi da questa risposta )

Come trovare la soluzione più breve

Di solito l'unico modo per trovare il metodo più breve è provarli tutti. Ciò comprende:

E queste sono solo le soluzioni più comuni; la soluzione migliore potrebbe essere una combinazione di questi o anche qualcosa di completamente diverso . Il modo migliore per trovare la soluzione più breve è provare tutto .


1
+1 per il suo valore e vorrei aggiungere un altro +1 per la zootopia
edc65

7

Modi più brevi di fare .replace


Se si desidera sostituire tutte le istanze di una sottostringa esatta con un'altra in una stringa, il modo ovvio sarebbe:

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

Tuttavia, è possibile ridurre di 1 byte:

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

Si noti che questo non è più breve se si desidera utilizzare funzionalità regex oltre alla gbandiera. Tuttavia, se stai sostituendo tutte le istanze di una variabile, di solito è molto più breve:

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!"

A volte vorrai mappare ogni carattere in una stringa, sostituendo ognuno con qualcos'altro. Mi trovo spesso a fare questo:

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

Tuttavia, .replaceè quasi sempre più breve:

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

Ora, se vuoi mappare ogni carattere in una stringa ma non ti interessa la stringa risultante, di .mapsolito è meglio perché puoi sbarazzarti di .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

Per l'ultimo caso, se /\w/gsono interessati solo alcuni caratteri che corrispondono a una regex (come ), l'uso di sostituire sarà molto meglio come in questa demo .
Shieru Asakoto,

6

Scrivere letterali RegEx con eval

Il costruttore regex può essere molto ingombrante a causa del suo nome lungo. Invece, scrivi un letterale con eval e backtick:

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

Se la variabile iè uguale a foo, questo genererà:

/<foo [^>]+/g

Questo è uguale a:

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

Puoi anche usare String.rawper evitare di dover sfuggire ripetutamente alle barre rovesciate\

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

Questo produrrà:

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

Che è uguale a:

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

Tieni a mente!

String.rawoccupa molti byte e, a meno che tu non abbia almeno nove barre rovesciate, String.rawsarà più lungo.


Non è necessario l'interno new, quindi l'utilizzo del costruttore è in realtà più breve per il secondo esempio
Ottimizzatore

5

.forEachvs forloop

Preferisco sempre .mapa qualsiasi per il ciclo. Risparmio facile e immediato.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • 8 byte totali per l'originale
  • 8 byte salvati contro for-of ( riduzione del 50% )
  • 22 byte salvati vs C-style per loop ( riduzione del 73% )

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • 16 byte totali per l'originale
  • 2 byte salvati contro for-of ( riduzione dell'11% )
  • 16 byte salvati vs C-style per loop ( riduzione del 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);
  • 22 byte totali per l'originale
  • 1 byte salvato vs for-in ( riduzione del 4% )
  • 11 byte salvati vs C-style per loop ( riduzione del 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);
  • 19 byte totali per l'originale
  • 2 byte salvati contro for-of ( riduzione del 10% )
  • 18 byte salvati vs C-style per loop ( riduzione del 49% )

5

Utilizzo di contatori non inizializzati in ricorsione

Nota : a rigor di termini, questo non è specifico di ES6. Ha più senso usare e abusare della ricorsione in ES6, tuttavia, a causa della natura concisa delle funzioni freccia.


È piuttosto comune imbattersi in una funzione ricorsiva che utilizza un contatore kinizialmente impostato su zero e incrementato ad ogni iterazione:

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

In determinate circostanze, è possibile omettere l'inizializzazione di tale contatore e sostituirlo k+1con -~k:

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

Questo trucco in genere consente di risparmiare 2 byte .

Perché e quando funziona?

La formula che lo rende possibile è ~undefined === -1. Quindi, alla prima iterazione, -~kverrà valutato 1. Nelle iterazioni successive, -~kè essenzialmente equivalente a -(-k-1)quale è uguale k+1, almeno per gli interi nell'intervallo [0… 2 31 -1].

È tuttavia necessario assicurarsi che k = undefinedla prima iterazione non interrompa il comportamento della funzione. Si dovrebbe in particolare tenere presente che la maggior parte delle operazioni aritmetiche che comporta undefinedcomporterà NaN.

Esempio 1

Dato un numero intero positivo n, questa funzione cerca il numero intero più piccolo kche non si divide n:

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

Può essere abbreviato in:

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

Questo funziona perché n % undefinedè NaN, il che è falso. Questo è il risultato atteso sulla prima iterazione.

[Link alla risposta originale]

Esempio n. 2

Dato un numero intero positivo n, questa funzione cerca un numero intero ptale che (3**p) - 1 == n:

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

Può essere abbreviato in:

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

Questo funziona perché pnon viene utilizzato affatto nella prima iterazione ( n<kessendo falso).

[Link alla risposta originale]


5

Funzioni ES6

Matematica

Math.cbrt(x)salva i caratteri di Math.pow(x,1/3).

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

3 caratteri salvati

Math.hypot(...args)è utile quando hai bisogno della radice quadrata della somma dei quadrati degli arg. Fare il codice ES5 per farlo è molto più difficile che usare un built-in.

La funzione Math.trunc(x)non sarebbe utile, poiché x|0è più breve. (Grazie Mwr247!)

Esistono molte proprietà che richiedono molto codice da eseguire in ES5, ma più semplici in ES6:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Calcola l'equivalente iperbolico delle funzioni trigonometriche.
  • Math.clz32. Potrebbe essere possibile farlo in ES5, ma ora è più facile. Conta gli zeri iniziali nella rappresentazione a 32 bit di un numero.

Ci sono un sacco di più, quindi sono solo andando a elencare alcuni:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)è quattro volte più lungo di x|0.
Mwr247,

@ mwr247: Ok, verrà aggiornato.
ev3commander,

Ecco gli equivalenti ES5 più brevi che conosco per un paio di queste funzioni: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 byte più lunghi; diventa ancora più lungo con più argomenti), Math.sign(a) => (a>0)-(a<0)(1 byte più corto, ma in alcuni casi necessita di parentesi circostanti; potrebbe non funzionare con NaN)
ETHproductions

@ETHproductions È necessario l'array degli argomenti per (la soluzione es5 di) hypot. E sei sicuro che la soluzione alternativa per Math.sign funzioni per -0? (Dovrebbe tornare a -0)
ev3commander il

1
@ ev3commander Questi sono solo intesi come sostituti in linea per i rispettivi equivalenti ES6, quindi sono ridimensionati per il 99% degli usi. Ricreare veramente queste funzioni richiederebbe molto più codice. Inoltre, non vedo alcun motivo per aver bisogno di avere un caso speciale per -0, poiché (AFAIK) non c'è modo di ottenere -0 se non specificandolo manualmente, e praticamente non serve a nulla all'interno di code-golf. Ma grazie per aver sottolineato quelle cose.
ETHproductions

5

Ottimizzazione di piccoli intervalli costanti per map()

Contesto

map()for[0..N1]

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

può essere sostituito da:

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

o più comunemente:

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

Array(N)N

[0..N1]

i

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 : la lunghezza del codice di richiamata F(i)non viene conteggiata.

[1..9]

[1..9]

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

Ottimizzazioni senza contatore

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 : la lunghezza del codice di richiamata F()non viene conteggiata.


Non dovrebbe 2**26essere 2**29?
Shaggy,

@Shaggy Heck. Buona pesca!
Arnauld,

Non volevo modificare in me stesso perché ho il daltonismo! : D
Shaggy,

Usando .keys(), non hai bisogno di un lambda:[...Array(10).keys()].map(do_something_with)
long-lazuli

@ long-lazuli Se non hai bisogno di una lambda e vuoi solo un intervallo, probabilmente non hai nemmeno bisogno di una mappa ...
Arnauld

4

Incarichi di ristrutturazione

ES6 introduce una nuova sintassi per le assegnazioni destrutturanti, ovvero tagliando un valore in pezzi e assegnando ogni pezzo a una variabile diversa. Ecco alcuni esempi:

Stringhe e 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

Oggetti

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

Queste assegnazioni possono essere utilizzate anche nei parametri di funzione:

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

Ancora un altro modo per evitare return

Sai che dovresti usare eval per le funzioni freccia con più istruzioni e un ritorno . In alcuni casi insoliti puoi risparmiare di più usando una sottofunzione interna.

Dico insolito perché

  1. Il risultato restituito non deve essere l'ultima espressione valutata nel ciclo

  2. Ci devono essere (almeno) 2 diverse inizializzazioni prima del ciclo

In questo caso è possibile utilizzare una sottofunzione interna senza ritorno, con uno dei valori iniziali passati come parametro.

Esempio Trova il reciproco della somma della funzione exp per i valori in un intervallo da a a b.

La lunga strada - 55 byte

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

Con eval - 54 byte

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

Con una funzione interna - 53 byte

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

Si noti che senza il requisito di un limite di intervallo inferiore a, posso unire le inizializzazioni di ie r e la versione di valutazione è più breve.


Nel tuo campione non è necessario conservarea
l4m2

@ l4m2 Non riesco a capire il tuo punto, aiuto per favore ...
edc65

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

@ l4m2 uh giusto, return a/rsarebbe un esempio migliore
edc65

1
eval è ancora meglio (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")e in questo caso(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon il

4

Utilizzo della sintassi del curry per funzioni diadiche e ricorsive

Funzioni diadiche

Ogni volta che una funzione accetta esattamente due argomenti senza valori predefiniti, l'utilizzo della sintassi del curry consente di salvare un byte.

Prima

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

Chiamato con f(a,b)

Dopo

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

Chiamato con f(a)(b)

Nota : questo post in Meta conferma la validità di questa sintassi.

Funzioni ricorsive

L'uso della sintassi del curry può anche salvare alcuni byte quando una funzione ricorsiva accetta diversi argomenti ma deve solo aggiornarne alcuni tra ogni iterazione.

Esempio

La seguente funzione calcola la somma di tutti i numeri interi nell'intervallo [a,b]:

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

Poiché arimane invariato durante l'intero processo, possiamo salvare 3 byte utilizzando:

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

Nota : come notato da Neil nei commenti, il fatto che un argomento non sia esplicitamente passato alla funzione ricorsiva non significa che debba essere considerato immutabile. Se necessario, potremmo modificare aall'interno del codice funzione con a++, a--o qualsiasi altra cosa simile sintassi.


L'ultimo esempio può essere scritto come a=>F=b=>a>b?0:a+++F(b), modificando aper ogni chiamata ricorsiva. Questo non aiuta in quel caso, ma potrebbe salvare byte in casi con più argomenti.
Neil,

Eh, stavo solo pensando di scrivere un suggerimento per questo :-)
ETHproductions

4

Funzione di test di primalità

La seguente funzione a 28 byte restituisce trueper i numeri primi e falseper i non primi:

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

Questo può essere facilmente modificato per calcolare altre cose. Ad esempio, questa funzione di 39 byte conta il numero di numeri primi minore o uguale a un numero:

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

Se hai già una variabile nche desideri verificare la primalità, la funzione di primalità può essere semplificata abbastanza:

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

Come funziona

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.

Nota: questo fallirà con un errore "troppa ricorsione" quando chiamato con un input sufficientemente grande, come 12345. Puoi aggirare questo con un ciclo:

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

1
Ma fallisci con troppa ricorsione per un input di appena 12345
edc65, il

x==1può probabilmente essere x<2per risparmiare.
CalculatorFeline

@CalculatorFeline Grazie, ma poi fallisce per 1o 0(perché xsarebbe 0o -1, rispettivamente)
ETHproductions

Potrebbe essere utile in alcuni casi. Inoltre, !~-xper -0 byte.
Calcolatrice

3

Array#concat() e l'operatore di diffusione

Ciò dipende in gran parte dalla situazione.


Combinazione di più array.

Preferisce la funzione concat a meno che la clonazione.

0 byte salvati

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

3 byte sprecati

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

3 byte salvati

a.concat()
[...a]

6 byte salvati

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

Preferisci usare un array già esistente a Array#concat().

Facile 4 byte salvati

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

3

Restituisce il risultato intermedio

Sai che usando l'operatore virgola puoi eseguire una sequenza di espressioni che restituiscono l'ultimo valore. Ma abusando della sintassi letterale dell'array, puoi restituire qualsiasi valore intermedio. È utile in .map () per esempio.

// 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
Ricorda, ovviamente, che .join('')può essere.join``
Cyoce,

3

Impostare i parametri di default della funzione

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

Questo è davvero utile ...

Tuttavia, assicurati di capire che qualcosa di simile _=>_||'asdf'è più breve quando passi solo un argomento (utile) alla funzione.


1
_=>_||'asdf'
Noterei

@Downgoat Vorrei notare che questo ritorna "asdf"per un input di ""(stringa vuota).
ETHproductions

2
Si noti che il valore predefinito viene valutato ogni volta che l'argomento sarebbe stato undefined, anche se si passa esplicitamente quel valore. Ad esempio, [...Array(n)].map((a,b,c)=>b)passa sempre undefinedper ae puoi quindi fornire un valore predefinito (anche se non in termini di b).
Neil,

3

Utilizzare al evalposto delle parentesi graffe per le funzioni freccia

Le funzioni delle frecce sono fantastiche. Prendono la forma x=>y, dove xè un argomento ed yè il valore restituito. Tuttavia, se è necessario utilizzare una struttura di controllo, ad esempio while, è necessario inserire parentesi graffe, ad es =>{while(){};return}. Tuttavia, possiamo aggirare questo; fortunatamente, la evalfunzione prende una stringa, la valuta come codice JS e restituisce l'ultima espressione valutata . Ad esempio, confronta questi due:

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

Possiamo usare un'estensione di questo concetto per abbreviare ulteriormente il nostro codice: agli occhi di eval, anche le strutture di controllo restituiscono la loro ultima espressione valutata. Per esempio:

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

3

Operazioni logiche di golf in ES6

"GLOE (S6)"

Logica generale

Di 'che hai costruito dichiarazioni se t. Verifica se puoi utilizzare uno dei seguenti sostituti:

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

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

(Questi potrebbero non funzionare se l'ordine è errato; cioè +e *hanno una precedenza di ordine inferiore rispetto ||e &&fare).

Inoltre, ecco alcune utili espressioni logiche:

  • Sia so tè vero / XOR:s^t
  • se thanno lo stesso valore di verità: !s^tos==t

Logica di array

Tutti i membri della acondizione soddisfano 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)

Almeno un membro asoddisfa la condizione 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)

Nessun membro della acondizione di soddisfare p: !a.some(p).

L'elemento eesiste nella matrice 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

Elemento enon non esiste nella matrice 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++]);

In genere uso &&e ||come x?y:xe x?x:y, rispettivamente. Ma posso vedere come ciò sarebbe utile in più programmi basati sulla logica. L'unico problema +sarebbe che esempio 3e -3sono entrambi truthy, ma 3+-3non è.
ETHproductions

@ETHproductions Ah, hai ragione; questo è un caso limite. -potrebbe anche funzionare, se s != t.
Conor O'Brien,

a.filter(t=>t==e).length==a.lengthnon è corretto. Dovrebbe essere!a.filter(t=>t==e).length
ETHproductions,

@ETHproductions hai ragione!
Conor O'Brien,

3

Ridurre le chiamate di funzione ripetute

Se hai ripetuto le chiamate a una funzione con un nome long-ish, come la manipolazione della tela:

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

Il modo tradizionale di abbreviare sarebbe quello di alias il nome della funzione:

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

Se hai abbastanza chiamate, un modo migliore è creare una funzione che fa il lavoro per te:

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

Se la maggior parte delle chiamate di funzione sono concatenate, è possibile fare in modo che la funzione restituisca se stessa, consentendo di eliminare due byte da ciascuna chiamata successiva:

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

Esempio di utilizzo: 1 , 2


1
puoi accorciare con l' operatore bind :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@Downgoat Grazie, quali browser lo supportano? (Inoltre, da quello che ho visto, si verificherà un errore nella seconda chiamata, poiché c.lineTonon restituisce naturalmente se stesso)
ETHproductions

devi sfregarlo attraverso la babele poiché è una funzione
ES7

3

Operatore di bind ::

L'operatore di bind può essere utilizzato per ridurre i byte rispetto a funzioni ripetute:

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

Inoltre, se si desidera utilizzare la funzione con un altro, thisad esempio:

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

Evitare le virgole quando si memorizzano molti dati

Se disponi di molti dati (ad es. Indici, caratteri, ...) che devi archiviare in un array, potresti fare meglio a lasciare tutte le virgole. Funziona meglio se ogni dato ha la stessa lunghezza di stringa, 1 ovviamente è ottimale.

43 byte (baseline)

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

34 byte (senza virgole)

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

Se si desidera modificare l'accesso all'array , è possibile ridurlo ulteriormente, memorizzando gli stessi valori in questo modo:

27 byte (stessi dati, modifica solo l'accesso all'array)

a=[..."376189452543276543"]

Perché viene evidenziato solo l'ultimo blocco?
Calcolatrice

@CalculatorFeline Grazie, risolto.
Chiru,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.