Dies ähnelt anderen "Tipps zum Golfen in <...>", zielt jedoch speziell auf die neueren Funktionen in JavaScript ab ECMAScript 6 ab.
JavaScript ist von Natur aus eine sehr ausführliche Sprache function(){}
, .forEach()
Umwandeln String - Array, Array-artiges Objekt Array, etc, etc Super bloats sind und nicht gesund für Golf.
ES6 + hingegen bietet einige sehr praktische Funktionen und eine geringere Stellfläche. x=>y
, [...x]
usw. sind nur einige Beispiele.
Bitte poste ein paar nette Tricks, die dabei helfen können, die wenigen zusätzlichen Bytes aus deinem Code zu entfernen.
HINWEIS: Tricks für ES5 sind bereits in Tipps zum Golfen in JavaScript verfügbar . Die Antworten auf diesen Thread sollten sich auf Tricks konzentrieren, die nur in ES6 und anderen zukünftigen ES-Versionen verfügbar sind.
Dieser Thread ist jedoch auch für Benutzer gedacht, die derzeit mit ES5-Funktionen Golf spielen. Die Antworten enthalten möglicherweise auch Tipps zum Verständnis und zur Zuordnung der ES6-Funktionen zu ihrem ES5-Codierungsstil.
quelle
Tricks, die ich hier gelernt habe, seit ich dazugekommen bin
Meine primäre Programmiersprache ist JS und hauptsächlich ES6. Seit ich vor einer Woche auf dieser Seite bin, habe ich viele nützliche Tricks von Kollegen gelernt. Einige davon kombiniere ich hier. Alle Kredite an die Community.
Pfeilfunktionen und Schleifen
Wir alle wissen, dass Pfeilfunktionen viele Bytes sparen
Aber Sie müssen ein paar Dinge beachten
,
klassifizieren:(a=b,a.map(d))
- Hier ist der zurückgegebene Wert der letzte Ausdrucka.map(d)
do something
Teil mehr als eine Anweisung ist, müssen Sie die umgebenden{}
Klammern hinzufügen .{}
Klammern gibt, müssen Sie eine explizite return-Anweisung hinzufügen.Das oben Gesagte trifft oft zu, wenn es sich um Schleifen handelt. Also so etwas wie:
Hier verschwende ich wegen der Rückgabe mindestens 9 Zeichen. Dies kann optimiert werden.
.map
oder.every
oder.some
. Beachten Sie, dass ein Fehler auftritt, wenn Sie dasselbe Array ändern möchten, über das Sie eine Zuordnung vornehmen.So wird das Obige:
entfernte Zeichen:
{}return
hinzugefügte Zeichen:
(){}>|
Beachten Sie, wie ich die Closure-Methode aufrufe, die die Variable korrekt auffüllt,
n
und da die Closure-Methode dann nichts zurückgibt (dh zurückgibtundefined
), bitweise oder es und das Arrayn
alle in einer einzigen Anweisung der äußeren Pfeilfunktion zurückgebeu
Kommas und Semikolons
Weiche ihnen aus,
Wenn Sie Variablen in einer Schleife deklarieren oder, wie im vorherigen Abschnitt erwähnt, mit
,
getrennten Anweisungen einzelne Anweisungspfeile verwenden, können Sie einige ziemlich raffinierte Tricks anwenden, um diese zu vermeiden,
oder;
die letzten paar Bytes zu entfernen.Betrachten Sie diesen Code:
Hier rufe ich viele Methoden auf, um viele Variablen zu initialisieren. Jede Initialisierung verwendet ein
,
oder;
. Dies kann wie folgt umgeschrieben werden:Beachten Sie, wie ich die Tatsache verwende, dass die Methode die an sie übergebene Variable nicht stört, und diese Tatsache verwende, um 3 Bytes zu rasieren.
Sonstiges
.search
Anstatt von.indexOf
Beide geben das gleiche Ergebnis, sind aber
search
kürzer. Die Suche erwartet zwar einen regulären Ausdruck, verwenden Sie ihn jedoch mit Bedacht.`Template Strings`
Diese sind sehr praktisch, wenn Sie einen oder mehrere Saitenteile unter bestimmten Bedingungen zusammenfügen müssen.
Nehmen Sie das folgende Beispiel, um ein Quine in JS auszugeben
gegen
In einer Vorlagenzeichenfolge, bei der es sich um eine Zeichenfolge in zwei Anführungszeichen (`) handelt, wird alles in a
${ }
als Code behandelt und ausgewertet, um die resultierende Antwort in die Zeichenfolge einzufügen.Ich werde später noch ein paar Tricks posten. Viel Spaß beim Golfen!
quelle
regexp
, keinen String. Try'abc'.search('.')
.map
ist Rekursion eine andere Technik , die manchmal können Ihnen helfen , eine Wende -for
Schleife in einen Ausdruck.Verwenden von Eigenschaftskürzeln
Mit Eigenschaftskurzbefehlen können Sie Variablen auf die Werte eines Arrays setzen:
Dies kann auch verwendet werden wie:
Sie können dies sogar verwenden, um Variablen umzukehren:
Hiermit können Sie auch
slice()
Funktionen verkürzen .Basis-Conversions
ES6 bietet eine viel kürzere Möglichkeit, die Formulare Base-2 (binär) und Base-8 (oktal) in Dezimalzahlen umzuwandeln:
+
kann verwendet werden, um eine binäre, oktale oder hexadezimale Zeichenfolge in eine Dezimalzahl umzuwandeln. Sie können verwendet werden0b
,0o
und0x
für Binär-, und hex jeweils .:Wenn Sie dies> 7 Mal verwenden, ist es kürzer, es zu verwenden
parseInt
und umzubenennen:Jetzt
p
könnenparseInt
Sie viele Bytes auf lange Sicht sparen.quelle
'0x'+v-0
ist noch kürzer, funktioniert aber in einigen Szenarien möglicherweise nicht so gut.0767
(ES5) kürzer als die0o767
(ES6) -Notation.0767
ist keine Standarderweiterung und im strikten Modus ausdrücklich verboten.0
- Vorgefertigte oktale Literale gehen nirgendwo hin und sind so gültig wie ecmascript0o
.Verwenden von Zeichenfolgenvorlagen mit Funktionen
Wenn Sie eine Funktion mit einer Zeichenfolge als Argument haben. Sie können das weglassen,
()
wenn Sie keine Ausdrücke haben:quelle
fun`string`
ist das gleiche wiefun(["string"])
, nichtfun("string")
. Dies ist in Ordnung für Funktionen, die in Zeichenfolgen umgewandelt werden, wiealert
z. B. , aber für andere kann dies zu Problemen führen. Weitere Informationen finden Sie im MDN-Artikelfun`foo${1}bar${2}baz
entspricht dem Aufruffun(["foo","bar","baz"],1,2)
Array-Verständnis (Firefox 30-57)
Hinweis: Das Array-Verständnis wurde nie standardisiert und wurde mit Firefox 58 überholt. Verwenden Sie es auf eigene Gefahr.
Ursprünglich enthielt die ECMAScript 7-Spezifikation eine Reihe neuer Array-basierter Funktionen. Obwohl die meisten davon nicht in die endgültige Version kamen, unterstützt Firefox (ed) möglicherweise die größte dieser Funktionen: Fantasievolle neue Syntax, die
.filter
und.map
durchfor(a of b)
Syntax ersetzen kann . Hier ist ein Beispiel:Wie Sie sehen, unterscheiden sich die beiden Zeilen nur geringfügig. Die zweite Zeile enthält keine umfangreichen Schlüsselwörter und Pfeilfunktionen. Dies erklärt aber nur die Reihenfolge
.filter().map()
; Was passiert, wenn Sie.map().filter()
stattdessen haben? Es kommt wirklich auf die Situation an:Oder was ist, wenn Sie entweder
.map
oder wollen.filter
? Nun, es stellt sich normalerweise als weniger OK heraus:Mein Rat ist also, Array-Verständnis dort zu verwenden, wo Sie es normalerweise verwenden würden,
.map
und.filter
nicht nur das eine oder das andere.String-Verständnis
Das Schöne am ES7-Verständnis ist, dass sie im Gegensatz zu Array-spezifischen Funktionen wie
.map
und.filter
für jedes iterierbare Objekt und nicht nur für Arrays verwendet werden können. Dies ist besonders nützlich, wenn Sie mit Zeichenfolgen arbeiten. Wenn Sie beispielsweise jedes Zeichenc
in einer Zeichenfolge ausführen möchten, gehen Sie wie folgt vorc.charCodeAt()
:Das sind zwei Bytes, die in relativ geringem Umfang eingespart werden. Und was ist, wenn Sie bestimmte Zeichen in einer Zeichenfolge filtern möchten? Zum Beispiel enthält dieser nur Großbuchstaben:
Hmm, das ist nicht kürzer. Aber wenn wir beides kombinieren:
Wow, ganze 10 Bytes gespart!
Ein weiterer Vorteil des Zeichenfolgenverständnisses besteht darin, dass fest codierte Zeichenfolgen ein zusätzliches Byte einsparen, da Sie das Leerzeichen weglassen können, nachdem
of
:Indizierung
Das Array-Verständnis macht es etwas schwieriger, den aktuellen Index in der Zeichenfolge / dem Array abzurufen. Dies kann jedoch folgendermaßen geschehen:
Das Wichtigste, worauf Sie achten müssen, ist sicherzustellen, dass der Index jedes Mal erhöht wird , nicht nur, wenn eine Bedingung erfüllt ist.
Verständnis des Generators
Generatorverständnisse haben grundsätzlich die gleiche Syntax wie Arrayverständnisse. Ersetzen Sie einfach die Klammern durch Klammern:
Dies erzeugt einen Generator , der ähnlich wie ein Array funktioniert, aber das ist eine Geschichte für eine andere Antwort.
Zusammenfassung
Grundsätzlich
.map().filter()
kommt es auf die Besonderheiten der Situation an , obwohl das Verständnis in der Regel kürzer ist als . Probieren Sie es am besten in beide Richtungen aus und finden Sie heraus, was besser funktioniert.PS Sie können gerne einen anderen verständnisbezogenen Tipp vorschlagen oder eine Möglichkeit finden, diese Antwort zu verbessern!
quelle
(x,y)=>[...Array(y-x)].map(a=>x++)
x=>[...Array(x).keys()]
n=>[for(x of Array(n).keys())if(/1/.test(x))x]
(spart 7 Bytes)Funktionsausdrücke in ES6 verwenden die Pfeilnotation und helfen viel, im Vergleich zur ES5-Version Bytes einzusparen:
Wenn Ihre Funktion nur einen Parameter hat, können Sie die Klammern weglassen, um zwei Bytes zu sparen:
Wenn Ihre Funktion überhaupt keine Parameter hat, deklarieren Sie sie so, als hätte sie einen, um ein Byte zu speichern:
Achtung: Die Pfeilfunktionen sind nicht identisch mit
function () {}
. Die Regeln fürthis
sind unterschiedlich (und besser IMO). Siehe Dokumentequelle
this
usw.f=(...x)=>x
würde , dassf(1,2,3) => [1,2,3]
.(x,y)=>...
, können Sie ein Byte durch Curry speichern, indem Sie es durchx=>y=>...
Verwenden
eval
für Pfeilfunktionen mit mehreren Anweisungen und areturn
Einer der lächerlicheren Tricks, über die ich gestolpert bin ...
Stellen Sie sich eine einfache Pfeilfunktion vor, die mehrere Anweisungen benötigt, und a
return
.Eine einfache Funktion, die einen einzelnen Parameter akzeptiert
a
, der alle Ganzzahlen[0, a)
durchläuft und sie am Ende der zurückgegebenen Ausgabezeichenfolge anhefteto
. Wenn Sie dies beispielsweise mit4
als Parameter aufrufen, erhalten Sie0123
.Beachten Sie, dass diese Pfeilfunktion in geschweifte Klammern eingeschlossen werden
{}
musste undreturn o
am Ende ein hat.Dieser erste Versuch wiegt 39 Bytes .
Nicht schlecht, aber mit
eval
können wir das verbessern.Diese Funktion entfernte die geschweiften Klammern und die return-Anweisung, indem sie den Code in ein
eval
umhüllte und einfach die letzte Anweisung in dereval
evalu to- Anweisung machteo
. Dies bewirkt dieeval
Rückgabe vono
, was wiederum die Rückgabe der Funktion bewirkto
, da es sich nun um eine einzelne Anweisung handelt.Dieser verbesserte Versuch hat ein Gewicht von 38 Byte und spart ein Byte gegenüber dem Original.
Aber warte, es gibt noch mehr! Eval-Anweisungen geben die zuletzt ausgewertete Anweisung zurück. In diesem Fall
o+=i
wertet das auso
, damit wir das nicht brauchen;o
! (Danke, edc65!)Dieser letzte Versuch wiegt nur 36 Bytes - eine Einsparung von 3 Bytes gegenüber dem Original!
Diese Technik kann auf alle allgemeinen Fälle ausgedehnt werden, in denen eine Pfeilfunktion einen Wert zurückgeben und mehrere Anweisungen haben muss (die auf andere Weise nicht kombiniert werden können).
wird
ein Byte speichern.
Wenn
statement2
bewertetv
, kann dies seinEinsparung von insgesamt 3 Bytes.
quelle
;o
- probieren Sie es aus:a=>eval('for(o="",i=0;i<a;i++)o+=i')
Neue Zeilen der Vorlagenzeichenfolge "\ n" vorziehen
Dies macht sich bereits bei einem einzelnen neuen Zeilenzeichen in Ihrem Code bezahlt. Ein Anwendungsfall könnte sein:
(16 Bytes)
(15 Bytes)
Update: Sie können sogar die geschweiften Klammern wegen markierter Template-Strings weglassen (danke, edc65!):
(13 Bytes)
quelle
Arrays füllen - Statische Werte und dynamische Bereiche
Ursprünglich habe ich diese als Kommentare unter Verstehen hinterlassen, aber da sich dieser Beitrag in erster Linie auf Verstehen konzentrierte, dachte ich, dass es gut wäre, diesem seinen eigenen Platz zu geben.
ES6 gab uns die Möglichkeit, Arrays ohne Schleifen mit statischen Werten zu füllen:
Beide geben ein Array der Länge x zurück, das mit dem Wert 0 gefüllt ist.
Wenn Sie Arrays jedoch mit dynamischen Werten füllen möchten (z. B. mit einem Bereich von 0 ... x), ist das Ergebnis etwas länger (obwohl es immer noch kürzer als der alte Weg ist):
Beide geben ein Array der Länge x zurück, beginnend mit dem Wert 0 und endend mit x-1.
Der Grund, warum Sie das
.fill()
dort benötigen, liegt darin, dass Sie es durch einfaches Initialisieren eines Arrays nicht zuordnen können. Dies bedeutet, dass beim Ausführenx=>Array(x).map((a,i)=>i)
ein leeres Array zurückgegeben wird. Sie können den Füllbedarf auch umgehen (und ihn damit noch kürzer machen), indem Sie den Spread-Operator wie folgt verwenden:Mit dem Operator und der
.keys()
Funktion spread können Sie jetzt einen kurzen Bereich von 0 ... x festlegen:Wenn Sie einen benutzerdefinierten Bereich von x ... y oder einen speziellen Bereich (z. B. gerade Zahlen) möchten, können Sie den Spread-Operator entfernen
.keys()
und einfach verwenden.map()
oder verwenden.filter()
:quelle
x=>Array(x).fill(i=0).map(a=>i++)
Außerdem bin ich mir nicht sicher, ob die Eingabe von 0.fill(0)
erforderlich ist. Hast du es ohne probiert?a=>a%2-1
funktioniert dies einwandfreia=>a%2<1
.[...Array(x)]
funktioniert genauso gut wieArray(x).fill()
und ist 2 Bytes kürzer.x=>[...Array(x)].map((a,i)=>i)
1/4
Beispiel wäre kürzer geschrieben[0,0,0,0]
und 2) stringifizierte Funktionen sind implementierungsspezifisch, geben also keine verlässliche Länge zurück (Map
32 Bytes in Chrome, aber 36 Bytes in Firefox).Rückgabe von Werten in Pfeilfunktionen
Es ist allgemein bekannt, dass eine einzelne Anweisung, die auf die Deklaration der Pfeilfunktion folgt, das Ergebnis dieser Anweisung zurückgibt:
-7 Bytes
Kombinieren Sie also nach Möglichkeit mehrere Anweisungen zu einer. Dies geschieht am einfachsten, indem die Anweisungen in Klammern eingeschlossen und durch Kommas getrennt werden:
-8 Bytes
Wenn es jedoch nur zwei Aussagen gibt, ist es normalerweise möglich (und kürzer), sie mit
&&
oder zu kombinieren||
:-9 Bytes
Wenn Sie eine Karte (oder ähnliches) verwenden und eine Nummer zurückgeben müssen und Sie garantieren können, dass die Karte niemals ein Array mit einer Länge von 1 und einer Nummer zurückgibt, können Sie die Nummer zurückgeben mit
|
:quelle
Zufällige Template-String-Hacks
Diese Funktion zerlegt zwei Saiten (dh wird
"abc","de"
zu"adbec"
):Beachten Sie, dass dies nur funktioniert, wenn
x
länger alsy
. Wie funktioniert es, fragst du?String.raw
ist so konzipiert, dass es ein Template-Tag ist:Dies nennt man im Grunde
String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y)
, obwohl es nicht so einfach ist. Das Template-Array hat auch eine spezielleraw
Eigenschaft, die im Grunde genommen eine Kopie des Arrays ist, jedoch mit den Roh-Strings.String.raw(x, ...args)
Grundsätzlich kehrtx.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...
und so weiter, bisx
keine Artikel mehr verfügbar sind.Nachdem wir nun wissen, wie es
String.raw
funktioniert, können wir es zu unserem Vorteil nutzen:Natürlich ist der letzte Teil
f=(x,y)=>x.split``.join(y)
viel kürzer, aber Sie haben die Idee.Hier sind ein paar Funktionen von riffling , die auch funktionieren , wenn
x
undy
sind gleich lang:Sie können mehr über
String.raw
MDN erfahren .quelle
Golfen mit Rekursion
Die Rekursion ist zwar nicht die schnellste, aber häufig die kürzeste. Im Allgemeinen ist die Rekursion am kürzesten, wenn die Lösung auf einen kleineren Teil der Herausforderung vereinfacht werden kann, insbesondere wenn die Eingabe eine Zahl oder eine Zeichenfolge ist. Wenn zum Beispiel
f("abcd")
aus"a"
und berechnet werden kannf("bcd")
, ist es normalerweise am besten, die Rekursion zu verwenden.Nehmen Sie zum Beispiel Fakultät:
In diesem Beispiel ist die Rekursion offensichtlich viel kürzer als bei jeder anderen Option.
Wie wäre es mit der Summe der Zeichencodes:
Diese ist kniffliger, aber wir können sehen, dass bei korrekter Implementierung durch Rekursion 4 Bytes eingespart werden
.map
.Betrachten wir nun die verschiedenen Arten der Rekursion:
Vorkursion
Dies ist normalerweise die kürzeste Art der Rekursion. Die Eingabe ist in zwei Teile
a
und aufgeteiltb
, und die Funktion berechnet etwas mita
undf(b)
. Zurück zu unserem Fakultätsbeispiel:In diesem Fall
a
ist n ,b
ist n-1 , und der Wert zurückgegeben wirda*f(b)
.Wichtiger Hinweis: Alle rekursiven Funktionen müssen die Möglichkeit haben, die Rekursion zu beenden, wenn die Eingabe klein genug ist. In der Fakultätsfunktion wird dies mit gesteuert
n? :1
, dh wenn der Eingang 0 ist , wird 1 zurückgegeben, ohnef
erneut aufzurufen .Nachrekursion
Die Nachrekursion ähnelt der Vorrekursion, unterscheidet sich jedoch geringfügig. Der Eingang ist in zwei Teile geteilt
a
undb
, und die Funktion berechnet etwas mita
, dann ruftf(b,a)
. Das zweite Argument hat normalerweise einen Standardwert (dhf(a,b=1)
).Eine Vorrekursion ist gut, wenn Sie mit dem Endergebnis etwas Besonderes anfangen müssen. Zum Beispiel, wenn Sie die Fakultät einer Zahl plus 1 wollen:
Selbst dann ist post- nicht immer kürzer als die Verwendung der Prärekursion in einer anderen Funktion:
Wann ist es also kürzer? Möglicherweise stellen Sie fest, dass nach der Rekursion in diesem Beispiel Klammern um die Funktionsargumente erforderlich sind, während dies vor der Rekursion nicht der Fall war. Wenn beide Lösungen Klammern um die Argumente benötigen, ist die Nachrekursion im Allgemeinen um 2 Byte kürzer:
(Programme hier aus dieser Antwort entnommen )
So finden Sie die kürzeste Lösung
Normalerweise können Sie die kürzeste Methode nur finden, indem Sie alle ausprobieren. Das beinhaltet:
.map
(entweder für Zeichenfolgen[...s].map
oders.replace
für Zahlen können Sie einen Bereich erstellen. )Und dies sind nur die gebräuchlichsten Lösungen. Die beste Lösung könnte eine Kombination davon sein oder sogar etwas ganz anderes . Der beste Weg, um die kürzeste Lösung zu finden, ist, alles auszuprobieren .
quelle
Kürzere Wege
.replace
Wenn Sie alle Instanzen einer exakten Teilzeichenfolge durch eine andere in einer Zeichenfolge ersetzen möchten, ist der naheliegende Weg:
Sie können jedoch 1 Byte kürzer machen:
Beachten Sie, dass dies nicht mehr kürzer ist, wenn Sie neben dem
g
Flag Regex-Funktionen verwenden möchten . Wenn Sie jedoch alle Instanzen einer Variablen ersetzen, ist diese normalerweise viel kürzer:Manchmal möchten Sie jedes Zeichen in einer Zeichenfolge abbilden und jedes durch etwas anderes ersetzen. Ich finde mich oft dabei:
Ist
.replace
aber fast immer kürzer:Wenn Sie nun jedes Zeichen in einer Zeichenfolge abbilden möchten, sich aber nicht für die resultierende Zeichenfolge interessieren,
.map
ist dies normalerweise besser, da Sie Folgendes entfernen können.join``
:quelle
/\w/g
) übereinstimmen, interessiert sind, ist die Verwendung von "Ersetzen" viel besser als in dieser Demo .RegEx-Literale schreiben mit
eval
Der Regex-Konstruktor kann aufgrund seines langen Namens sehr sperrig sein. Schreiben Sie stattdessen ein Literal mit eval und backticks:
Wenn die Variable
i
gleich istfoo
, generiert dies:Dies ist gleich:
Sie können auch verwenden
String.raw
, um zu vermeiden, dass Backslashes wiederholt ausgeblendet werden müssen\
Dies wird Folgendes ausgeben:
Welches ist gleich:
Merken Sie sich!
String.raw
nimmt eine Menge Bytes in Anspruch und wird länger sein , es sei denn, Sie haben mindestens neun BackslashesString.raw
.quelle
new
in dort nicht, so dass die Verwendung des Konstruktors für das zweite Beispiel tatsächlich kürzer ist.forEach
vsfor
SchleifenImmer
.map
einer for-Schleife vorziehen . Einfache und sofortige Einsparungen.quelle
Verwenden von nicht initialisierten Zählern in der Rekursion
Hinweis : Genau genommen ist dies nicht ES6-spezifisch. Es ist jedoch sinnvoller, die Rekursion in ES6 zu verwenden und zu missbrauchen, da die Pfeilfunktionen prägnant sind.
Es ist eher üblich, auf eine rekursive Funktion zu stoßen, die einen Zähler verwendet, der
k
anfänglich auf Null gesetzt und bei jeder Iteration inkrementiert wurde:Unter bestimmten Umständen ist es möglich , die Initialisierung eines solchen Zählers wegzulassen und ersetzen
k+1
mit-~k
:Dieser Trick spart normalerweise 2 Bytes .
Warum und wann funktioniert es?
Die Formel, die es möglich macht, ist
~undefined === -1
. So wird bei der ersten Iteration-~k
ausgewertet1
. Auf den nächsten Iterationen-~k
entspricht im Wesentlichen-(-k-1)
die gleichk+1
, zumindest für ganze Zahlen im Bereich [0 ... 2 31 -1].Sie müssen jedoch sicherstellen, dass
k = undefined
bei der ersten Iteration das Verhalten der Funktion nicht gestört wird. Beachten Sie insbesondere, dass die meisten arithmetischen Operationenundefined
dazu führen werdenNaN
.Beispiel 1
Bei einer positiven Ganzzahl
n
sucht diese Funktion nach der kleinsten Ganzzahlk
, die sich nicht teiltn
:Es kann gekürzt werden auf:
Das funktioniert , weil
n % undefined
istNaN
, was falsy ist. Das ist das erwartete Ergebnis bei der ersten Iteration.[Link zur ursprünglichen Antwort]
Beispiel # 2
Bei einer positiven Ganzzahl
n
sucht diese Funktion nach einer Ganzzahlp
wie folgt(3**p) - 1 == n
:Es kann gekürzt werden auf:
Dies funktioniert, weil
p
es bei der ersten Iteration überhaupt nicht verwendet wird (n<k
weil es falsch ist).[Link zur ursprünglichen Antwort]
quelle
ES6 funktioniert
Mathematik
Math.cbrt(x)
speichert Zeichen alsMath.pow(x,1/3)
.3 Zeichen gespeichert
Math.hypot(...args)
ist nützlich, wenn Sie die Quadratwurzel der Summe der Quadrate der Args benötigen. Die Erstellung von ES5-Code ist viel schwieriger als die Verwendung eines integrierten Codes.Die Funktion
Math.trunc(x)
wäre nicht hilfreich, da siex|0
kürzer ist. (Danke Mwr247!)Es gibt viele Eigenschaften, für die in ES5 viel Code erforderlich ist, in ES6 ist dies jedoch einfacher:
Math.acosh
,asinh
,atanh
,cosh
,sinh
,tanh
. Berechnet das hyperbolische Äquivalent von trigonometrischen Funktionen.Math.clz32
. Könnte in ES5 möglich sein, ist aber jetzt einfacher. Zählt führende Nullen in der 32-Bit-Darstellung einer Zahl.Es gibt viel mehr, so dass ich nur einige gehe zur Liste:
Math.sign
,Math.fround
,Math.imul
,Math.log10
,Math.log2
,Math.log1p
.quelle
Math.trunc(x)
ist viermal länger alsx|0
.Math.hypot(a,b) => Math.sqrt(a*a+b*b)
(3 Byte länger; wird mit mehr Argumenten noch länger)Math.sign(a) => (a>0)-(a<0)
(1 Byte kürzer, benötigt aber in einigen Fällen umgebende Klammern; funktioniert möglicherweise nichtNaN
)Optimierung kleiner konstanter Bereiche für
map()
Kontext
map()
for
kann ersetzt werden durch:
oder häufiger:
Array(N)
NB : Die Länge des Rückrufcodes
F(i)
wird nicht gezählt.Optimierungen ohne Zähler
NB : Die Länge des Rückrufcodes
F()
wird nicht gezählt.quelle
2**26
sein2**29
?.keys()
, brauchen Sie kein Lambda:[...Array(10).keys()].map(do_something_with)
Aufgaben zerstören
ES6 führt eine neue Syntax ein, um Zuweisungen zu destrukturieren, dh einen Wert in Stücke zu schneiden und jedes Stück einer anderen Variablen zuzuweisen. Hier einige Beispiele:
Streicher und Arrays
Objekte
Diese Zuweisungen können auch in Funktionsparametern verwendet werden:
quelle
Noch ein anderer Weg, um zu vermeiden
return
Sie wissen, dass Sie eval für Pfeilfunktionen mit mehreren Anweisungen und einem Return verwenden sollten . In einigen ungewöhnlichen Fällen können Sie mit einer inneren Unterfunktion mehr sparen.
Ich sage ungewöhnlich, weil
Das zurückgegebene Ergebnis darf nicht der letzte in der Schleife ausgewertete Ausdruck sein
Vor der Schleife müssen (mindestens) 2 verschiedene Initialisierungen vorhanden sein
In diesem Fall können Sie eine innere Unterfunktion ohne Rückgabe verwenden, wobei einer der Anfangswerte als Parameter übergeben wird.
Beispiel Ermitteln Sie den Kehrwert der Summe der Exp-Funktionen für Werte in einem Bereich von a bis b.
Der lange Weg - 55 Bytes
Mit eval - 54 Bytes
Mit einer inneren Funktion - 53 Bytes
Beachten Sie, dass
a
ich ohne die Anforderung einer unteren Bereichsgrenze die Initialisierungen von i und r zusammenführen kann und die Evaluierungsversion kürzer ist.quelle
a
(i,b)=>{for(r=0;i<=b;i++)r+=Math.exp(i);return 1/r}
return a/r
wäre ein besseres Beispiel(a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")
und in diesem Fall(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
Verwenden der Currying-Syntax für dyadische und rekursive Funktionen
Dyadische Funktionen
Immer wenn eine Funktion genau zwei Argumente ohne Standardwerte verwendet, spart die aktuelle Syntax ein Byte.
Vor
Angerufen mit
f(a,b)
Nach
Angerufen mit
f(a)(b)
Hinweis : Dieser Beitrag in Meta bestätigt die Gültigkeit dieser Syntax.
Rekursive Funktionen
Durch die Verwendung der currying-Syntax werden möglicherweise auch einige Bytes gespart, wenn eine rekursive Funktion mehrere Argumente verwendet, diese jedoch nur zwischen den einzelnen Iterationen aktualisiert werden müssen.
Beispiel
Die folgende Funktion berechnet die Summe aller Ganzzahlen im Bereich
[a,b]
:Da dies
a
während des gesamten Vorgangs unverändert bleibt, können wir 3 Bytes einsparen, indem wir Folgendes verwenden:Anmerkung : Wie Neil in den Kommentaren bemerkt hat, bedeutet die Tatsache, dass ein Argument nicht explizit an die rekursive Funktion übergeben wird, nicht, dass es als unveränderlich angesehen werden sollte. Bei Bedarf könnten wir ändern
a
innerhalb des Funktionscode mita++
,a--
oder was auch immer ähnlicher Syntax.quelle
a=>F=b=>a>b?0:a+++F(b)
, dass esa
für jeden rekursiven Aufruf geändert wird. In diesem Fall hilft dies nicht, in Fällen mit mehr Argumenten werden jedoch möglicherweise Bytes gespart.Primalitätstestfunktion
Die folgende 28-Byte-Funktion gibt
true
für Primzahlen undfalse
für Nicht-Primzahlen zurück:Dies kann leicht geändert werden, um andere Dinge zu berechnen. Diese 39-Byte-Funktion zählt beispielsweise die Anzahl der Primzahlen, die kleiner oder gleich einer Zahl sind:
Wenn Sie bereits eine Variable haben
n
, die Sie auf Primalität prüfen möchten, kann die Primalitätsfunktion erheblich vereinfacht werden:Wie es funktioniert
Hinweis: Bei Aufrufen mit einer ausreichend großen Eingabe, z. B. 12345, schlägt dies mit dem Fehler "zu viel Rekursion" fehl. Sie können dies mit einer Schleife umgehen:
quelle
x==1
kann wahrscheinlichx<2
für Einsparungen sein.1
oder0
(weilx
wäre0
oder-1
)!~-x
für -0 Bytes.Array#concat()
und der Spread-OperatorDies hängt weitgehend von der Situation ab.
Mehrere Arrays kombinieren.
Bevorzugen Sie die concat-Funktion, es sei denn, Sie klonen.
0 Bytes gespeichert
3 Bytes verschwendet
3 Bytes gespeichert
6 Bytes gespeichert
Verwenden Sie lieber ein bereits vorhandenes Array als
Array#concat()
.Einfache 4 Bytes gespeichert
quelle
Zwischenergebnis zurückgeben
Sie wissen, dass Sie mit dem Komma-Operator eine Folge von Ausdrücken ausführen können, die den letzten Wert zurückgeben. Wenn Sie jedoch die Literal-Array-Syntax missbrauchen, können Sie einen beliebigen Zwischenwert zurückgeben. Es ist zum Beispiel in .map () nützlich.
quelle
.join('')
kann natürlich sein.join``
Standardeinstellungen für Funktionsparameter festlegen
Das ist wirklich nützlich ...
Stellen Sie jedoch sicher, dass so etwas
_=>_||'asdf'
kürzer ist, wenn Sie nur ein (nützliches) Argument an die Funktion übergeben.quelle
_=>_||'asdf'
in den meisten Fällen kürzer ist"asdf"
für eine Eingabe von""
(leere Zeichenfolge) zurückgibt .undefined
, auch wenn Sie diesen Wert explizit übergeben. Beispielsweise wird[...Array(n)].map((a,b,c)=>b)
immerundefined
als übergebena
, und Sie können daher einen Standardwert dafür angeben (obwohl nicht in Bezug aufb
).Verwenden Sie
eval
anstelle von geschweiften Klammern PfeilfunktionenPfeilfunktionen sind fantastisch. Sie haben die Form
x=>y
, in derx
ein Argument undy
der Rückgabewert stehen. Wenn Sie jedoch eine Kontrollstruktur verwenden müssen, wie z. B.while
, müssen Sie geschweifte Klammern setzen, z=>{while(){};return}
. Wir können dies jedoch umgehen; Zum Glück nimmt dieeval
Funktion einen String, wertet diesen String als JS-Code aus und gibt den zuletzt ausgewerteten Ausdruck zurück . Vergleichen Sie zum Beispiel diese beiden:Wir können eine Erweiterung dieses Konzepts verwenden, um unseren Code weiter zu verkürzen: In den Augen von geben
eval
Kontrollstrukturen auch ihren zuletzt ausgewerteten Ausdruck zurück. Zum Beispiel:quelle
Golf logische Operationen in ES6
GLOE (S6)
Allgemeine Logik
Angenommen, Sie haben Anweisungen erstellt
s
undt
. Prüfen Sie, ob Sie einen der folgenden Ersatzartikel verwenden können:(Diese können nicht funktionieren , wenn die Reihenfolge falsch ist, dh
+
und*
eine niedrigere Ordnung Vorrang als||
und&&
tun.)Hier sind auch einige nützliche logische Ausdrücke:
s
odert
ist wahr / XOR:s^t
s
undt
sind die gleichen Wahrheitswert:!s^t
oders==t
Array-Logik
Alle Mitglieder von
a
erfüllen Bedingungp
:Mindestens ein Mitglied
a
erfüllt die Bedingungp
:Keine Mitglieder
a
satisfy Zustandp
:!a.some(p)
.Element
e
existiert im Arraya
:Element
e
ist nicht vorhanden in Arraya
:quelle
&&
und||
wiex?y:x
undx?x:y
dargestellt. Aber ich kann sehen, wie nützlich dies in logikbasierten Programmen wäre. Das einzige Problem mit+
wäre, dass zB3
und-3
beide wahr sind, aber3+-3
nicht.-
könnte auch funktionieren, wenns != t
.a.filter(t=>t==e).length==a.length
ist falsch. Es sollte sein!a.filter(t=>t==e).length
Verkürzen Sie wiederholte Funktionsaufrufe
Wenn Sie eine Funktion mit einem langen Namen wiederholt aufgerufen haben, z. B. Canvas-Manipulation:
Die traditionelle Art, es zu verkürzen, wäre der Alias des Funktionsnamens:
Wenn Sie genügend Anrufe haben, ist es besser, eine Funktion zu erstellen, die die Arbeit für Sie erledigt:
Wenn die meisten Funktionsaufrufe verkettet sind, können Sie die Funktion selbst zurückgeben lassen, sodass Sie zwei Bytes von jedem nachfolgenden Aufruf abschneiden können:
Anwendungsbeispiel: 1 , 2
quelle
(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
c.lineTo
sich natürlich nicht von selbstOperator binden
::
Der Bindeoperator kann verwendet werden, um Bytes bei wiederholten Funktionen zu verkürzen:
Zusätzlich, wenn Sie die Funktion mit einem anderen verwenden möchten,
this
zB:quelle
Vermeiden Sie Kommas, wenn Sie viele Daten speichern
Wenn Sie viele Daten (z. B. Indizes, Zeichen usw.) in einem Array speichern müssen, sollten Sie möglicherweise alle Kommas weglassen. Dies funktioniert am besten, wenn jedes Datenelement dieselbe Zeichenfolgenlänge hat, wobei 1 offensichtlich optimal ist.
43 Bytes (Grundlinie)
34 Bytes (keine Kommas)
Wenn Sie Ihren Array-Zugriff ändern möchten , können Sie diesen möglicherweise noch weiter reduzieren, indem Sie dieselben Werte wie folgt speichern:
27 Bytes (gleiche Daten, ändert nur den Array-Zugriff)
quelle