Conseils pour jouer au golf dans ECMAScript 6 et plus


88

Cette procédure est similaire aux autres "Conseils pour jouer au golf dans <...>", mais elle cible spécifiquement les nouvelles fonctionnalités JavaScript introduites dans ECMAScript 6 et versions ultérieures.

JavaScript est intrinsèquement un langage très prolixe function(){},.forEach() , chaîne de conversion à un tableau, un objet semblable à un tableau de tableau, etc, etc sont bloats super et pas sain pour le golf.

L'ES6 +, en revanche, présente des fonctionnalités extrêmement pratiques et un encombrement réduit. x=>y,[...x] etc. ne sont que quelques exemples.

S'il vous plaît postez quelques astuces qui peuvent aider à réduire ces quelques octets supplémentaires de votre code.

REMARQUE: les astuces pour ES5 sont déjà disponibles dans Astuces pour le golf en JavaScript. . les réponses à ce fil doivent se concentrer sur des astuces uniquement disponibles dans ES6 et d'autres versions futures d'ES.

Toutefois, ce fil est également destiné aux utilisateurs qui utilisent actuellement les fonctionnalités ES5 pour jouer au golf. Les réponses peuvent également contenir des conseils pour les aider à comprendre et à mapper les fonctionnalités de ES6 à leur style de codage ES5.

Réponses:


42

Opérateur de propagation ...

L'opérateur spread transforme une valeur de tableau en une liste séparée par des virgules.

Cas d'utilisation 1:

Utiliser directement un tableau où une fonction attend une liste

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

Cas d'utilisation 2:

Créer un littéral de tableau à partir d'un itérable (généralement une chaîne)

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

Cas d'utilisation 3:

Déclarer un nombre variable d'arguments pour une fonction

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

Voir mozilla doc


3
Maintenant, j'ai un vote négatif ici. Il est évident que quelqu'un a noté quelque chose de terriblement faux dans cette astuce, étant trop timide pour laisser un commentaire et expliquer ...
edc65

Ça a l'air bien. Peut-être que c'était le manque de points-virgules? ;) (d'ailleurs, vous pouvez aussi l'utiliser comme paramètres de repos, comme les splats dans Ruby)
gcampbell

Vous pouvez ajouter qu'il existe également un cas d'utilisation dans les signatures de fonctions :)
Felix Dombek

Mauvais clic n'a pas voulu dire
Stan Strum

@StanStrum ça arrive. Je ferai une petite mise à jour de ce message pour que vous puissiez éventuellement modifier votre vote (ou vous l'avez déjà fait?)
edc65

21

Trucs appris ici depuis mon arrivée

Mon langage de programmation principal est JS et principalement ES6. Depuis que j'ai rejoint ce site il y a une semaine, j'ai appris beaucoup de trucs utiles de la part de mes collègues. Je combine certains de ceux ici. Tous les crédits à la communauté.

Fonctions de flèche et boucles

Nous savons tous que les fonctions de flèche permettent d'économiser beaucoup de byts

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

Mais vous devez garder à l'esprit quelques petites choses

  • Essayez d'associer plusieurs déclarations à l'aide de , ie (a=b,a.map(d))- Ici, la valeur renvoyée est la dernière expression.a.map(d)
  • si votre do somethingpartie est plus d'une déclaration, alors vous devez ajouter le entourage{} parenthèses.
  • S'il y a des environs {} crochets, vous devez ajouter une instruction de retour explicite.

Ce qui précède s’applique bien souvent lorsque des boucles sont impliquées. Donc, quelque chose comme:

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

Ici, je gaspille au moins 9 caractères à cause du retour. Cela peut être optimisé.

  • Essayez d'éviter les boucles. Utiliser .mapou.every ou à la .someplace. Notez que si vous souhaitez modifier le même tableau sur lequel vous mappez, cela échouera.
  • Enroulez la boucle dans une fonction de flèche de fermeture, en convertissant la fonction de flèche principale en une seule instruction.

Donc ce qui précède devient:

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]

caractères supprimés: {}return

caractères ajoutés: (){}>|

Notez comment j'appelle la méthode de fermeture, qui remplit correctement la variable n, puis, puisque la méthode de fermeture ne renvoie rien (c.-à-d undefined., Retourne), j'utilise bitwise ou elle et renvoie le tableau ndans une seule instruction de la fonction de flèche extérieure.u

Virgules et points-virgules

Evite-les,

Si vous déclarez des variables dans une boucle, ou comme mentionné dans la section précédente, en utilisant ,des instructions séparées pour avoir des fonctions de flèche d'instructions simples, vous pouvez utiliser quelques astuces assez astucieuses pour les éviter ,ou ;pour supprimer les derniers octets.

Considérons ce code:

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

Ici, j'appelle beaucoup de méthodes pour initialiser beaucoup de variables. Chaque initialisation utilise un ,ou ;. Cela peut être réécrit comme:

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

Notez comment j'utilise le fait que la méthode ne gêne pas la variable qui lui est transmise et utilisez ce fait pour supprimer 3 octets.

Misc

.search au lieu de .indexOf

Les deux donnent le même résultat mais searchsont plus courts. Bien que la recherche attende une expression régulière, utilisez-la à bon escient.

`Template Strings`

Celles-ci sont très pratiques lorsque vous devez concaténer une ou plusieurs parties de cordes en fonction de certaines conditions.

Prenons l'exemple suivant pour sortir une quine dans JS

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

contre.

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

Dans une chaîne de modèle, qui est une chaîne à l'intérieur de deux guillemets arrières (`), tout élément à l'intérieur d'un élément ${ }est traité comme un code et évalué pour insérer la réponse résultante dans la chaîne.

Je posterai quelques astuces plus tard. Bon golf!


1
.search est plus court, utilisez-le quand c'est possible! mais ce n'est pas la même chose de .indexOf. .search veut un regexp, pas une chaîne. Essayez'abc'.search('.')
edc65

@ edc65 Mis à jour!
Optimiseur

Vous pouvez modifier le tableau d'origine avec les méthodes d'instance. Le second est l'index actuel et le troisième est le tableau en itération.
Isiah Meadows

8
"Rejoint le site il y a une semaine" - 21.4k rep ...
GamrCorps

2
De plus .map, la récursivité est une autre technique qui peut parfois vous aider à transformer une forboucle en expression.
Neil

20

Utiliser des raccourcis de propriété

Les raccourcis de propriété vous permettent de définir des variables sur les valeurs d'un tableau:

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

Cela peut aussi être utilisé comme:

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

Vous pouvez même l'utiliser pour inverser des variables:

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

Vous pouvez également l'utiliser pour raccourcir les slice()fonctions.

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

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

Conversions de base

ES6 fournit un moyen beaucoup plus court de convertir les formulaires Base-2 (binaire) et Base-8 (octal) en décimal:

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

+peut être utilisé pour convertir une chaîne binaire, octale ou hexadécimale en un nombre décimal. Vous pouvez utiliser 0b, 0oet 0x, respectivement pour binaire, octal et 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

Si vous utilisez ceci> 7 fois, il sera alors plus court à utiliser parseIntet à le renommer:

(p=parseInt)(v,2)

Vous ppouvez maintenant utiliser cette option pour parseIntéconomiser de nombreux octets sur le long terme.


L'astuce de conversion de base est intéressante, mais il est plus probable que le numéro de conversion soit sous la forme d'une variable au lieu d'un littéral, auquel cas, il devient beaucoup plus long.
Optimiseur

1
'0x'+v-0est même plus court, mais peut ne pas fonctionner aussi bien dans certains scénarios.
ETHproductions

1
Au fait, 0767(ES5) est plus court que la 0o767notation (ES6).
Camilo Martin

@CamiloMartin 0767est une extension non standard, elle est explicitement interdite en mode strict.
Oriol

1
Le mode strict de @Oriol était mauvais. Cela n’a pas aidé les performances, ne vous a pas vraiment obligé à écrire un bon code et ne deviendrait jamais le cas par défaut. 0Les littéraux octaux pré-préfixés ne vont nulle part et sont aussi valables que ecmascript 0o.
Camilo Martin

19

Utilisation de modèles de chaîne avec des fonctions

Lorsque vous avez une fonction avec une chaîne comme arguments. Vous pouvez omettre le ()si vous n'avez aucune expression:

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

9
Soyez averti, cela passe en réalité un tableau. fun`string` est le même que fun(["string"]), pas fun("string"). Cela convient pour les fonctions qui transtypent en chaîne, par exemple alert, mais pour d’autres, cela pourrait poser des problèmes. Pour plus d'informations, voir l'article de MDN
Cyoce

5
Chose de référence rapide: fun`foo${1}bar${2}bazéquivaut à appelerfun(["foo","bar","baz"],1,2)
Cyoce

14

Compréhensions des tableaux (Firefox 30-57)

Remarque: la compréhension des tableaux n’a jamais été normalisée et est devenue obsolète avec Firefox 58. Utilisez-le à vos risques et périls.


A l'origine, la spécification ECMAScript 7 contenait un ensemble de nouvelles fonctionnalités basées sur des tableaux. Bien que la plupart d’entre elles n’aient pas été intégrées à la version finalisée, Firefox supportait (peut-être) la plus grande de ces fonctionnalités: une nouvelle syntaxe élégante qui peut remplacer .filteret .mapavec la for(a of b)syntaxe. Voici un exemple:

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

Comme vous pouvez le constater, les deux lignes ne sont pas si différentes que la seconde ne contenant pas les mots-clés volumineux et les fonctions de flèche. Mais cela ne représente que la commande .filter().map(); que se passe-t-il si vous en avez à la .map().filter()place? Cela dépend vraiment de la situation:

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]

Ou si vous voulez soit .map ou .filter ? Eh bien, il s’avère généralement moins 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]

Donc , mon conseil est d'utiliser du tableau où compréhensions vous habituellement utiliser .map et .filter , mais pas seulement l' un ou l'autre.

String Compréhensions

Une bonne chose à propos des interprétations d'ES7 est que, contrairement aux fonctions spécifiques aux tableaux telles que .mapet .filter, elles peuvent être utilisées sur n'importe quel objet itératif, pas seulement sur des tableaux. Ceci est particulièrement utile lorsqu'il s'agit de chaînes. Par exemple, si vous souhaitez exécuter chaque caractère cd'une chaîne via c.charCodeAt():

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

C'est deux octets sauvés sur une assez petite échelle. Et si vous voulez filtrer certains caractères d'une chaîne? Par exemple, celui-ci ne conserve que des lettres majuscules:

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

Hmm, ce n'est pas plus court. Mais si nous combinons les deux:

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

Wow, 10 octets sauvés!

Un autre avantage de la compréhension des chaînes est que les chaînes codées en dur enregistrent un octet supplémentaire, car vous pouvez omettre l'espace après 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]

Indexage

Les compréhensions de tableau rendent un peu plus difficile d'obtenir l'index actuel dans la chaîne / tableau, mais cela peut être fait:

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

La principale chose à faire est de s’assurer que l’index est incrémenté à chaque fois, pas seulement lorsqu’une condition est remplie.

Compréhensions du générateur

Les compréhensions de générateur ont fondamentalement la même syntaxe que les compréhensions de tableau; remplacez simplement les crochets par des parenthèses:

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

Cela crée un générateur qui fonctionne à peu près de la même manière qu'un tableau, mais c'est une histoire pour une autre réponse.

Sommaire

Fondamentalement, bien que les compréhensions soient généralement plus courtes que tout .map().filter(), tout dépend des spécificités de la situation. Il est préférable d'essayer les deux manières et voir ce qui fonctionne le mieux.

PS N'hésitez pas à suggérer un autre conseil relatif à la compréhension ou un moyen d'améliorer cette réponse!


Voici un truc pour les gammes qui sauveront quelques personnages de plus:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
Vous pouvez couper 11 autres octets pour créer une plage de 0 à x:x=>[...Array(x).keys()]
Mwr247

Dernier point pour la compréhension là-bas: n=>[for(x of Array(n).keys())if(/1/.test(x))x](sauvegarde de 7 octets)
Mwr247

@ Mwr247 En fait, je peux maintenant voir que les gammes ne sont généralement pas aussi courtes en termes de compréhension que d'autres fonctionnalités de l'ES6. Je vais plutôt ajouter une section sur les chaînes et vous laisser gérer les plages.
ETHproductions

Il est à noter que Array Comprehensions est obsolète et supprimé de toutes les versions récentes de javascript. Voir la documentation MDN sur le sujet.
Keefer Rourke

13

Les expressions de fonction dans ES6 utilisent la notation en flèche, ce qui permet d'économiser beaucoup d'octets par rapport à la version ES5:

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

Si votre fonction n'a qu'un seul paramètre, vous pouvez omettre les parenthèses pour enregistrer deux octets:

f=x=>x+1

Si votre fonction n'a aucun paramètre, déclarez-la comme si elle en avait un pour enregistrer un octet:

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

Attention: les fonctions des flèches ne sont pas exactement les mêmes que function () {}. Les règles pour thissont différentes (et meilleure IMO). Voir docs


2
Mais quand vous jouez au golf, vous ne vous souciez généralement pas de thisetc.
Optimizer

1
Généralement pas, mais c’est une mise en garde que vous ne saurez peut-être jamais quand elle se présentera. Il est également plus courant que les lambdas n'aient pas besoin d'une fonction locale pour cette liaison dans la production.
Isiah Meadows

En outre, si vous souhaitez prendre tous vos arguments, vous pouvez utiliser la fonction d'argument "reste", par exemple, f=(...x)=>x aurait cela f(1,2,3) => [1,2,3].
Conor O'Brien

1
Voici un conseil spécifique à ce site: si vous répondez avec une fonction qui prend la forme, (x,y)=>...vous pouvez enregistrer un octet avec curry en le remplaçant parx=>y=>...
Cyoce

12

Utilisation evaldes fonctions de flèche avec plusieurs instructions et unreturn

Un des trucs les plus ridicules que je suis tombé sur ...

Imaginez une simple fonction de flèche nécessitant plusieurs déclarations et a return.

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

Une fonction simple acceptant un seul paramètre a, qui itère sur tous les entiers de [0, a)et les pointe à la fin de la chaîne de sortie o, qui est renvoyée. Par exemple, appeler ceci avec 4comme paramètre produirait 0123.

Notez que cette fonction de flèche devait être entourée d’accolades {}et avoir unreturn o à la fin.

Cette première tentative pèse 39 octets .

Pas mal, mais en utilisant eval, nous pouvons améliorer cela.

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

Cette fonction supprime les accolades et l’instruction return en encapsulant le code dans un evalet en faisant simplement la dernière instruction de l’ evalévaluation o. Cela provoque le evalretour o, ce qui à son tour provoque le retour de la fonction o, puisqu'il s'agit maintenant d'une seule instruction.

Cette tentative améliorée pèse 38 octets , économisant un octet de l'original.

Mais attendez, il y a plus! Les instructions Eval renvoient quelle que soit leur dernière instruction évaluée. Dans ce cas, o+=iévalue à o, nous n'avons donc pas besoin du ;o! (Merci, edc65!)

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

Cette dernière tentative ne pèse que 36 octets - une économie de 3 octets par rapport à l'original!


Cette technique peut être étendue à tous les cas généraux où une fonction de flèche doit renvoyer une valeur et avoir plusieurs instructions (qui ne pourraient pas être combinées par d'autres moyens).

b=>{statement1;statement2;return v}

devient

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

enregistrer un octet.

Si statement2évalue à v, cela peut être

b=>eval('statement1;statement2')

économiser un total de 3 octets.


1
Je pense qu'écrire une fonction anonyme pourrait être encore plus court
Rédigé

@vihan ouais, ces deux fonctions pourraient être rendues anonymes pour économiser 2 octets chacune. Les économies d'un octet sont toujours valables.
jrich

1
Mais encore mieux: eval renvoie la dernière expression évaluée, vous n'avez donc pas besoin de l' ;oessayer:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
Mais des chaînes de template!
Conor O'Brien

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ Vous souhaitez expliquer comment les chaînes de modèle fonctionneraient ici en utilisant la fonction d'exemple en tant que contexte?
WallyWest

10

Préférez les nouvelles lignes de la chaîne de modèle à "\ n"

Cela commencera à porter ses fruits même avec un seul caractère de nouvelle ligne dans votre code. Un cas d'utilisation pourrait être:

(16 octets)

array.join("\n")

(15 octets)

array.join(`
`)

Mise à jour: Vous pouvez même laisser les accolades à cause des chaînes de modèles étiquetées (merci, edc65!):

(13 octets)

array.join`
`

5
Mais encore mieux, vous pouvez éviter la parenthèse. Lisez la documentation ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) pour savoir pourquoi
Edc65

Ah, d'accord. Merci, je l'ai ajouté.
Chiru

9

Remplissage des tableaux - Valeurs statiques et plages dynamiques

Au départ, je les ai laissés sous la forme de commentaires, mais comme cet article était principalement axé sur la compréhension, je me suis dit que ce serait bien de lui donner sa place.

ES6 nous a donné la possibilité de remplir des tableaux avec des valeurs statiques sans utiliser de boucles:

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

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

Les deux renvoient un tableau de longueur x, rempli avec la valeur 0.

Si vous souhaitez remplir des tableaux avec des valeurs dynamiques (comme une plage de 0 ... x), le résultat est un peu plus long (bien qu'il soit encore plus court que l'ancien):

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

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

Tous deux renvoient un tableau de longueur x, commençant par la valeur 0 et se terminant par x-1.

La raison pour laquelle vous avez besoin .fill()de là est parce que simplement initialiser un tableau ne vous laissera pas le mapper. C'est-à-dire que faire x=>Array(x).map((a,i)=>i)retournera un tableau vide. Vous pouvez également contourner le besoin de remplissage (et le rendre ainsi encore plus court) en utilisant l'opérateur de propagation comme suit:

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

En utilisant l'opérateur et la .keys()fonction spread , vous pouvez maintenant créer une courte plage 0 ... x:

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

Si vous voulez une plage personnalisée de x ... y, ou une plage spécialisée (comme des nombres pairs), vous pouvez vous débarrasser de .keys()et simplement utiliser .map(), ou utiliser .filter(), avec l'opérateur de propagation:

// 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)

Voici une suggestion pour le deuxième exemple: x=>Array(x).fill(i=0).map(a=>i++)De plus, je ne suis pas sûr que le 0 dans .fill(0)soit nécessaire. Avez-vous essayé sans?
ETHproductions

@ETHproductions Vous avez raison, j'ai oublié que le 0 n'est pas nécessaire dans le remplissage avant la carte. Cela fait cependant 1 caractère plus court que votre suggéré, donc je vais le garder comme ça. Merci!
Mwr247

En outre, pour le dernier exemple, a=>a%2-1fonctionne bien, comme le fait a=>a%2<1.
ETHproductions

1
Une nouvelle astuce que j'ai apprise: [...Array(x)]fonctionne aussi bien Array(x).fill()et est plus courte de 2 octets. x=>[...Array(x)].map((a,i)=>i)
ETHproductions

1
@yonatanmn Très sympa! Seuls les commentaires seraient 1) l' 1/4exemple serait plus court [0,0,0,0]et 2) les fonctions stringifiées sont spécifiques à l'implémentation. Par conséquent, elles ne renverront pas une longueur fiable ( Map32 octets dans Chrome, mais 36 octets dans Firefox).
Mwr247

9

Renvoi de valeurs dans les fonctions de flèche

Il est de notoriété publique que si une seule instruction suit la déclaration de la fonction de flèche, elle renvoie le résultat de cette instruction:

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

-7 octets

Donc, lorsque cela est possible, combinez plusieurs déclarations en une seule. Cela se fait plus facilement en entourant les instructions de parenthèses et en les séparant par des virgules:

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

-8 octets

Mais s'il n'y a que deux déclarations, il est généralement possible (et plus court) de les combiner avec &&ou ||:

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 octets

Enfin, si vous utilisez map (ou similaire) et que vous avez besoin de renvoyer un nombre et que vous puissiez vous assurer que la carte ne renverra jamais un tableau de 1 longueur avec un nombre, vous pouvez renvoyer le nombre avec |:

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

Dans ce dernier exemple, vous devez également vous assurer que le nombre sera toujours un entier.
ETHproductions

8

Hacks aléatoires de chaînes de modèles

Cette fonction génère deux chaînes (c.-à-d. Se transforme "abc","de"en "adbec"):

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

Notez que cela ne fonctionne que quand xest plus long que y. Comment ça marche, demandez-vous? String.rawest conçu pour être une balise de modèle, comme suit:

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

Ceci appelle essentiellement String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), bien que ce ne soit pas si simple. Le tableau de modèles a également une rawpropriété spéciale , qui est essentiellement une copie du tableau, mais avec les chaînes brutes. String.raw(x, ...args)revient essentiellement x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...et ainsi de suite jusqu'à épuisement xdes éléments.

Alors maintenant que nous savons comment ça String.rawmarche, nous pouvons l’utiliser à notre avantage:

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"

Bien sûr, pour ce dernier, f=(x,y)=>x.split``.join(y)c'est beaucoup plus court, mais vous voyez l'idée.

Voici quelques fonctions qui fonctionnent aussi si xet yont la même longueur:

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

Vous pouvez en apprendre plus String.raw sur MDN .


7

Comment jouer au golf avec récursion

La récursion, bien que n'étant pas l'option la plus rapide, est très souvent la plus courte. En règle générale, la récursivité est la plus courte si la solution peut être simplifiée, mais surtout si l’entrée est un nombre ou une chaîne. Par exemple, si f("abcd")peut être calculé à partir de "a"etf("bcd") , il est généralement préférable d'utiliser la récursivité.

Prenons, par exemple, factorielle:

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

Dans cet exemple, la récursivité est évidemment beaucoup plus courte que toute autre option.

Que diriez-vous de la somme des charcodes:

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

Celui-ci est plus compliqué, mais nous pouvons voir que lorsqu'il est correctement implémenté, la récursivité économise 4 octets .map.

Voyons maintenant les différents types de récursivité:

Pré-récursion

C'est généralement le type de récursivité le plus court. L'entrée est divisée en deux parties aet b, et la fonction calcule quelque chose avec aet f(b). Revenons à notre exemple factoriel:

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

Dans ce cas, aest n , best n-1 , et la valeur retournée est a*f(b).

Remarque importante: Toutes les fonctions récursives doivent avoir un moyen d'arrêter de récursir lorsque l'entrée est suffisamment petite. Dans la fonction factorielle, ceci est contrôlé avec le n? :1, c'est-à-dire que si l'entrée est 0 , retourne 1 sans appeler à fnouveau.

Post-récurrence

La post-récurrence est similaire à la pré-récursion, mais légèrement différente. L'entrée est divisée en deux parties aet b, et la fonction calcule quelque chose avec a, puis appelle f(b,a). Le second argument a généralement une valeur par défaut (ie f(a,b=1)).

La pré-récursivité est bonne lorsque vous devez faire quelque chose de spécial avec le résultat final. Par exemple, si vous voulez la factorielle d’un nombre plus 1:

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

Cependant, même dans ce cas, la post-utilisation n’est pas toujours plus courte que l’utilisation de la pré-récursion dans une autre fonction:

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

Alors, quand est-ce plus court? Vous remarquerez peut-être que la post-récursion dans cet exemple nécessite des parenthèses autour des arguments de la fonction, contrairement à la pré-récursion. En règle générale, si les deux solutions nécessitent des parenthèses autour des arguments, la post-récurrence est environ 2 octets plus courte:

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

(programmes ici tirés de cette réponse )

Comment trouver la solution la plus courte

Habituellement, la seule façon de trouver la méthode la plus courte est de toutes les essayer. Ceci comprend:

Et ce ne sont que les solutions les plus courantes; la meilleure solution pourrait être une combinaison de ceux-ci, ou même quelque chose de complètement différent . La meilleure façon de trouver la solution la plus courte est de tout essayer .


1
+1 pour sa valeur, et j'aimerais ajouter un autre +1 pour zootopia
edc65

7

Des façons plus courtes de faire .replace


Si vous souhaitez remplacer toutes les occurrences d'une sous-chaîne exacte par une autre dans une chaîne, la solution évidente serait la suivante:

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

Cependant, vous pouvez faire un octet plus court:

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

Notez que cela n’est plus plus court si vous souhaitez utiliser des fonctionnalités regex autres que le gdrapeau. Cependant, si vous remplacez toutes les occurrences d'une variable, celle-ci est généralement beaucoup plus courte:

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

Parfois, vous aurez envie de mapper chaque caractère d'une chaîne en remplaçant chaque caractère par quelque chose d'autre. Je me retrouve souvent à faire ça:

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

Cependant, .replaceest presque toujours plus courte:

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

Maintenant, si vous voulez mapper chaque caractère d'une chaîne sans vous soucier de la chaîne résultante, il .mapest généralement préférable de se débarrasser de .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

Dans le dernier cas, si seuls certains caractères correspondant à une expression rationnelle (comme /\w/g) sont intéressés, utiliser remplacer sera beaucoup mieux comme dans cette démo .
Shieru Asakoto

6

Écrire des littéraux RegEx avec eval

Le constructeur de regex peut être très volumineux à cause de son nom long. Au lieu de cela, écrivez un littéral avec eval et backticks:

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

Si la variable iest égale à foo, cela va générer:

/<foo [^>]+/g

Ceci est égal à:

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

Vous pouvez également utiliser String.rawpour éviter d'avoir à échapper à plusieurs reprises des barres obliques inverses\

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

Cela produira:

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

Qui est égal à:

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

Garder en tete!

String.rawprend beaucoup d'octets et à moins que vous ayez au moins neuf barres obliques inverses, ce String.rawsera plus long.


Vous n'avez pas besoin newde là dedans, donc utiliser le constructeur est en fait plus court pour le deuxième exemple
Optimizer

5

.forEach contre for boucles

Toujours préférer .mapà n'importe quelle boucle. Économies faciles et instantanées.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • 8 octets au total pour l'original
  • 8 octets enregistrés vs pour-de ( réduction de 50% )
  • 22 octets enregistrés par rapport au style C pour la boucle ( réduction de 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 octets au total pour l'original
  • 2 octets économisés vs pour-de ( réduction de 11% )
  • 16 octets enregistrés par rapport au style C pour la boucle ( réduction de 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 octets au total pour l'original
  • 1 octet enregistré vs pour-in ( 4% réduction de )
  • 11 octets enregistrés par rapport au style C pour la boucle ( réduction de 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 octets au total pour l'original
  • 2 octets enregistrés vs pour-de ( réduction de 10% )
  • 18 octets enregistrés par rapport au style C pour la boucle ( réduction de 49% )

5

Utilisation de compteurs non initialisés en récursivité

Remarque : Strictement parlant, ceci n'est pas spécifique à ES6. Cependant, il est plus logique d'utiliser et d'abuser de la récursivité dans ES6 en raison de la nature concise des fonctions fléchées.


Il est assez courant de rencontrer une fonction récursive qui utilise un compteur kinitialement défini à zéro et incrémenté à chaque itération:

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

Dans certaines circonstances, il est possible d'omettre l'initialisation d'un tel compteur et de le remplacer k+1par -~k:

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

Cette astuce enregistre généralement 2 octets .

Pourquoi et quand ça marche?

La formule qui le rend possible est ~undefined === -1. Donc, à la première itération, -~ksera évalué à 1. Sur les itérations suivantes, -~kest essentiellement équivalent à -(-k-1)qui égalek+1 , au moins pour les entiers compris dans l'intervalle [0… 2 31 -1].

Vous devez cependant vous assurer que k = undefinedla première itération ne perturbera pas le comportement de la fonction. Il faut surtout garder à l’esprit que la plupart des opérations arithmétiques impliquantundefined aboutiront à NaN.

Exemple 1

Étant donné un entier positif n, cette fonction recherche le plus petit entier kqui ne se divise pas n:

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

Il peut être réduit à:

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

Cela fonctionne parce que n % undefinedestNaN , ce qui est de la fausseté. C'est le résultat attendu à la première itération.

[Lien vers la réponse originale]

Exemple n ° 2

Étant donné un entier positif n, cette fonction recherche un entier ptel que (3**p) - 1 == n:

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

Il peut être réduit à:

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

Cela fonctionne car il pn'est pas utilisé du tout à la première itération ( n<kétant faux).

[Lien vers la réponse originale]


5

Fonctions ES6

Math

Math.cbrt(x)enregistre des caractères que Math.pow(x,1/3).

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

3 caractères sauvegardés

Math.hypot(...args)est utile lorsque vous avez besoin de la racine carrée de la somme des carrés des arguments. Il est beaucoup plus difficile de créer du code ES5 que d’utiliser un logiciel intégré.

La fonction Math.trunc(x)ne serait pas utile, car x|0est plus courte. (Merci Mwr247!)

Il existe de nombreuses propriétés qui nécessitent beaucoup de code dans ES5, mais plus faciles dans ES6:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Calcule l'équivalent hyperbolique des fonctions trigonométriques.
  • Math.clz32. Cela pourrait être possible dans ES5, mais c'est plus facile maintenant. Compte les zéros en tête dans la représentation 32 bits d'un nombre.

Il y a beaucoup plus, donc je vais juste énumérer quelques - unes:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)est quatre fois plus long que x|0.
Mwr247

@ mwr247: Ok, va mettre à jour.
ev3commander

Voici les équivalents ES5 les plus courts que je connaisse pour quelques- unes de ces fonctions: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 octets de plus; devient encore plus long avec plus d'arguments), Math.sign(a) => (a>0)-(a<0)(1 octet de moins, mais il faut parfois entourer les parenthèses; peut ne pas fonctionner NaN)
ETHproductions

@ETHproductions Vous avez besoin du tableau d'arguments pour (la solution de contournement es5 de) hypot. Et êtes-vous sûr que la solution de contournement pour Math.sign fonctionne pour -0? (Il devrait retourner -0)
ev3commander

1
@ ev3commander Il s'agit simplement de remplacer en ligne leurs équivalents ES6 respectifs. Ils sont donc réduits pour 99% des utilisations. Recréer véritablement ces fonctions nécessiterait beaucoup plus de code. De plus, je ne vois aucune raison d'avoir besoin d'un cas spécial pour -0, car (autant que je sache, il est impossible d'obtenir -0 si ce n'est en le spécifiant manuellement et pratiquement pas d'utilisation dans le code-golf. Mais merci d'avoir signalé ces choses.
ETHproductions

5

Optimisation de petites plages constantes pour map()

Le contexte

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

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

peut être remplacé par:

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

ou plus communément:

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

Array(N)N

[0 ..N-1]

je

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 longueur du code de rappel F(i)n'est pas comptée.

[1..9]

[1..9]

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

Optimisations sans compteur

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 longueur du code de rappel F()n'est pas comptée.


Ne devrait pas 2**26être 2**29?
Shaggy

@ Shaggy Heck. Bonne prise!
Arnauld

Je ne voulais pas éditer moi-même parce que je suis aveuglé par le code! : D
Shaggy

En utilisant .keys(), vous n'avez pas besoin d'un lambda:[...Array(10).keys()].map(do_something_with)
long-lazuli

@ long-lazuli Si vous n'avez pas besoin d'un lambda et que vous voulez juste une plage, vous n'avez probablement pas besoin de carte non plus ...
Arnauld

4

Missions de restructuration

ES6 introduit une nouvelle syntaxe pour les assignations de déstructuration, c'est-à-dire couper une valeur en morceaux et affecter chaque morceau à une variable différente. Voici quelques exemples:

Cordes et tableaux

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

Objets

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

Ces affectations peuvent également être utilisées dans les paramètres de fonction:

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

Encore un autre moyen d'éviter return

Vous savez que vous devriez utiliser eval pour les fonctions de flèche avec plusieurs déclarations et un retour . Dans certains cas inhabituels, vous pouvez économiser davantage en utilisant une sous-fonction interne.

Je dis inhabituel parce que

  1. Le résultat renvoyé ne doit pas être la dernière expression évaluée dans la boucle.

  2. Il doit y avoir (au moins) 2 initialisations différentes avant la boucle

Dans ce cas, vous pouvez utiliser une sous-fonction interne sans retour, avec l'une des valeurs initiales passée en paramètre.

Exemple Trouver l'inverse de la fonction sum of exp pour des valeurs comprises entre a et b.

Le long chemin - 55 octets

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

Avec eval - 54 octets

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

Avec une fonction interne - 53 octets

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

Notez que sans l'exigence d'une limite inférieure a, je peux fusionner les initialisations de i et r et la version eval est plus courte.


Dans votre échantillon, il n'est pas nécessaire de gardera
l4m2

@ l4m2 Je ne peux pas comprendre ce que vous voulez, aidez
moi

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

@ l4m2 euh à droite, return a/rserait un meilleur exemple
edc65

1
eval est toujours meilleur (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")et dans ce cas(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon

4

Utilisation de la syntaxe de currying pour les fonctions dyadiques et récursives

Fonctions dyadiques

Lorsqu'une fonction prend exactement deux arguments sans valeur par défaut, la syntaxe de curry enregistre un octet.

Avant

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

Appelé avec f(a,b)

Après

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

Appelé avec f(a)(b)

Note : Ce post dans Meta confirme la validité de cette syntaxe.

Fonctions récursives

L'utilisation de la syntaxe de currying peut également permettre d'économiser quelques octets lorsqu'une fonction récursive prend plusieurs arguments, mais ne nécessite que de mettre à jour certains d'entre eux entre chaque itération.

Exemple

La fonction suivante calcule la somme de tous les entiers compris dans la plage [a,b]:

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

Comme areste inchangé pendant tout le processus, nous pouvons économiser 3 octets en utilisant:

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

Remarque : comme Neil l'a remarqué dans les commentaires, le fait qu'un argument ne soit pas explicitement passé à la fonction récursive ne signifie pas qu'il devrait être considéré comme immuable. Si nécessaire, nous pourrions modifier ale code de fonction avec a++, a--ou une syntaxe similaire.


Le dernier exemple peut être écrit ainsi a=>F=b=>a>b?0:a+++F(b), en modifiant apour chaque appel récursif. Dans ce cas, cela n’aide en rien, mais cela pourrait économiser des octets dans les cas avec plus d’arguments.
Neil

Heh, je pensais juste écrire un conseil pour cela :-)
ETHproductions

4

Fonction de test de primalité

La fonction suivante sur 28 octets est renvoyée truepour les nombres premiers et falsepour les non-premiers:

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

Cela peut facilement être modifié pour calculer d'autres choses. Par exemple, cette fonction de 39 octets compte le nombre de nombres premiers inférieurs ou égaux à un nombre:

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

Si vous avez déjà une variable nà vérifier pour la primalité, la fonction de primalité peut être simplifiée un peu:

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

Comment ça fonctionne

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.

Remarque: cela échouera avec une erreur "trop ​​de récursivité" s'il est appelé avec une entrée suffisamment grande, telle que 12345. Vous pouvez contourner ce problème avec une boucle:

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

1
Mais échouer avec trop de récursivité pour une entrée aussi petite que 12345
edc65

x==1peut probablement être x<2pour des économies.
CalculatriceFeline

@CalculatorFeline Merci, mais ensuite il échoue pour 1ou 0(parce que xserait 0ou -1, respectivement)
ETHproductions

Pourrait être utile dans certains cas. Aussi, !~-xpour -0 octets.
CalculatriceFeline

3

Array#concat() et l'opérateur de propagation

Cela dépend en grande partie de la situation.


Combinaison de plusieurs tableaux.

Préférez la fonction concat sauf clonage.

0 octet enregistré

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

3 octets gaspillés

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

3 octets enregistrés

a.concat()
[...a]

6 octets enregistrés

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

Préférez utiliser un tableau déjà existant pour Array#concat() .

Facile 4 octets enregistrés

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

3

Retour du résultat intermédiaire

Vous savez qu'en utilisant l'opérateur virgule, vous pouvez exécuter une séquence d'expressions renvoyant la dernière valeur. Mais en abusant de la syntaxe de tableau littéral, vous pouvez renvoyer n'importe quelle valeur intermédiaire. C'est utile dans .map () par exemple.

// 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
Rappelez-vous, bien sûr, cela .join('')peut être.join``
Cyoce

3

Définir les paramètres par défaut de la fonction

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

Celui-ci est vraiment utile ...

Cependant, assurez-vous de comprendre que quelque chose comme _=>_||'asdf'est plus court lorsque vous ne passez qu'un argument (utile) à la fonction.


1
Je noterais que l'utilisation d'un bloc _=>_||'asdf'
opératoire

@Downgoat Je noterais que cela retourne "asdf"pour une entrée de ""(chaîne vide).
ETHproductions

2
Notez que la valeur par défaut est évaluée chaque fois que l’argument aurait été undefined , même si vous transmettez explicitement cette valeur. Par exemple, [...Array(n)].map((a,b,c)=>b)passe toujours undefinedpour a, et vous pouvez donc lui fournir une valeur par défaut (mais pas en termes de b).
Neil

3

Utilisez evalau lieu d'accolades pour les fonctions de flèche

Les fonctions fléchées sont géniales. Ils prennent la forme x=>y, où xest un argument et yest la valeur de retour. Cependant, si vous devez utiliser une structure de contrôle, telle que while, vous devez mettre des accolades, par exemple =>{while(){};return}. Cependant, nous pouvons contourner cela; Heureusement, la evalfonction prend une chaîne, l'évalue en tant que code JS et renvoie la dernière expression évaluée . Par exemple, comparez ces deux:

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

Nous pouvons utiliser une extension de ce concept pour raccourcir davantage notre code: aux yeux de eval, les structures de contrôle renvoient également leur dernière expression évaluée. Par exemple:

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

3

Opérations logiques de golf dans ES6

"GLOE (S6)"

Logique générale

Supposons que vous avez construit des déclarations set t. Voyez si vous pouvez utiliser l'un des remplacements suivants:

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

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

(Celles-ci peuvent ne pas fonctionner si l’ordre est mauvais; c’est +-à- dire et *ont une priorité inférieure à celle de|| et &&faire.)

En outre, voici quelques expressions logiques utiles:

  • soit sout est vrai / XOR:s^t
  • set tont la même valeur de vérité: !s^tous==t

Logique de tableau

Tous les membres de asatisfaire la condition 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)

Au moins un membre de asatisfait à la condition 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)

Aucun membre de acondition satisfaite p:!a.some(p) .

L'élément eexiste dans le tableau 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

L'élément en'existe pas dans le tableau 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++]);

J'utilise généralement &&et ||comme x?y:xet x?x:y, respectivement. Mais je peux voir en quoi cela serait utile dans des programmes plus logiques. Le seul problème +serait que, par exemple 3, ils -3sont tous deux véridiques, mais 3+-3ne l’est pas.
ETHproductions

@ETHproductions Ah, tu as raison; c'est un cas de bord. -pourrait aussi fonctionner, si s != t.
Conor O'Brien

a.filter(t=>t==e).length==a.lengthest incorrect. Il devrait être!a.filter(t=>t==e).length
ETHproductions

@ETHproductions vous avez raison!
Conor O'Brien

3

Raccourcir les appels de fonction répétés

Si vous avez répété des appels à une fonction portant un nom long, telle que la manipulation de canevas:

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

La manière traditionnelle de le raccourcir serait d’aliaser le nom de la fonction:

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

Si vous avez suffisamment d'appels, une meilleure solution consiste à créer une fonction qui effectue le travail à votre place:

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

Si la plupart des appels de fonction sont chaînés, vous pouvez faire en sorte que la fonction se retourne elle-même, ce qui vous permet de couper deux octets de chaque appel successif:

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

Exemple d'utilisation: 1 , 2


1
vous pouvez raccourcir avec l' opérateur bind :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat le

@Downgoat Merci, quels navigateurs supportent cela? (En outre, d'après ce que j'ai vu, cela entraînera une erreur lors du deuxième appel, car c.lineToil ne revient pas naturellement lui-même)
ETHproductions

tu dois le frotter à travers babel car c'est une fonctionnalité de l'ES7
Downgoat le

3

Opérateur de liaison ::

L'opérateur bind peut être utilisé pour aider à raccourcir les octets par rapport aux fonctions répétées:

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

De plus, si vous souhaitez utiliser la fonction avec un autre, thispar exemple:

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

Éviter les virgules lors du stockage de nombreuses données

Si vous avez besoin de stocker beaucoup de données (index, caractères,…) dans un tableau, il vaudrait peut-être mieux laisser toutes les virgules à distance. Cela fonctionne mieux si chaque donnée a la même longueur de chaîne, 1 étant évidemment optimale.

43 octets (base)

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

34 octets (sans virgule)

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

Si vous êtes prêt à modifier votre accès au tableau , vous pouvez le réduire davantage en stockant les mêmes valeurs, comme ceci:

27 octets (mêmes données, ne modifie que l'accès au tableau)

a=[..."376189452543276543"]

Pourquoi seul le dernier bloc est-il sélectionné?
CalculatriceFeline

@CalculatorFeline Merci, corrigé.
Chiru
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.