Jest to podobne do innych „Porad do gry w golfa w <...>”, ale w szczególności dotyczy nowych funkcji JavaScript włączonych w ECMAScript 6 i nowszych.
JavaScript jest językiem natury bardzo gadatliwy, function(){}
, .forEach()
, przekształcając ciąg do tablicy, tablicy podobny obiekt do tablicy, etc, etc są super bloats i nie zdrowe dla golfa.
Z drugiej strony ES6 + ma kilka bardzo przydatnych funkcji i ma mniejszą powierzchnię. x=>y
, [...x]
itp. to tylko niektóre przykłady.
Proszę zamieścić kilka fajnych sztuczek, które mogą pomóc w usunięciu kilku dodatkowych bajtów z twojego kodu.
UWAGA: Triki dla ES5 są już dostępne w Poradach dotyczących gry w golfa w JavaScript ; odpowiedzi na ten wątek powinny koncentrować się na sztuczkach dostępnych tylko w ES6 i innych przyszłych wersjach ES.
Jednak ten wątek jest również dla użytkowników, którzy obecnie grają w golfa za pomocą funkcji ES5. Odpowiedzi mogą również zawierać wskazówki, które pomogą im zrozumieć i odwzorować funkcje ES6 na ich styl kodowania ES5.
źródło
Sztuczki nauczyłem się tutaj, odkąd dołączyłem
Moim podstawowym językiem programowania jest JS i głównie ES6. Odkąd dołączyłem do tej witryny tydzień temu, nauczyłem się wielu przydatnych sztuczek od innych członków. Łączę niektóre z nich tutaj. Wszystkie kredyty dla społeczności.
Funkcje strzałek i pętle
Wszyscy wiemy, że funkcje strzałek pozwalają zaoszczędzić wiele bajtów
Ale musisz pamiętać o kilku rzeczach
,
np.(a=b,a.map(d))
- Tutaj zwracana wartość jest ostatnim wyrażeniema.map(d)
do something
część jest więcej niż jedną instrukcją, musisz dodać otaczające{}
nawiasy.{}
nawiasy otaczające , musisz dodać jawną instrukcję return.Powyższe sprawdza się wiele razy, gdy masz zaangażowane pętle. Więc coś takiego:
Tutaj marnuję co najmniej 9 znaków z powodu zwrotu. Można to zoptymalizować.
.map
lub.every
lub.some
zamiast. Pamiętaj, że jeśli chcesz zmienić tę samą tablicę, na której mapujesz, to się nie powiedzie.Tak więc powyższe staje się:
usunięte postacie:
{}return
dodane znaki:
(){}>|
Zwróć uwagę, jak wywołuję metodę zamknięcia, która poprawnie wypełnia zmienną,
n
a następnie, ponieważ metoda zamknięcia nic nie zwraca (tj. Zwracaundefined
), bitowo lub to i zwracam tablicęn
w jednym poleceniu funkcji zewnętrznej strzałkiu
Przecinki i średniki
Unikaj ich co kiedykolwiek,
Jeśli deklarujesz zmienne w pętli lub, jak wspomniano w poprzedniej sekcji, używając
,
oddzielnych instrukcji, aby mieć funkcje strzałek pojedynczej instrukcji, możesz użyć całkiem sprytnych sztuczek, aby ich uniknąć,
lub;
ogolić kilka ostatnich bajtów.Rozważ ten kod:
Wzywam tutaj wiele metod inicjowania wielu zmiennych. Każda inicjalizacja używa znaku
,
lub;
. Można to przepisać jako:Zauważ, że wykorzystuję fakt, że metoda nie przeszkadza przekazanej mu zmiennej, i używam tego faktu do golenia 3 bajtów.
różne
.search
zamiast.indexOf
Oba dają ten sam wynik, ale
search
są krótsze. Chociaż wyszukiwanie oczekuje wyrażenia regularnego, używaj go mądrze.`Ciągi szablonów`
Są one bardzo przydatne, gdy trzeba połączyć jedną lub więcej części struny na podstawie określonych warunków.
Weź poniższy przykład, aby wyprowadzić quine w JS
vs.
W ciągu szablonu, który jest ciągiem wewnątrz dwóch cudzysłowów (`), wszystko wewnątrz a
${ }
jest traktowane jako kod i analizowane pod kątem wstawienia wynikowej odpowiedzi do ciągu.Później dodam kilka trików. Miłej gry w golfa!
źródło
regexp
nie łańcucha. Spróbuj'abc'.search('.')
.map
rekurencja jest inną techniką, która może czasem pomóc ci zmienićfor
pętlę w wyrażenie.Używanie skrótów własności
Skrócone właściwości pozwalają ustawić zmienne na wartości tablic:
Można to również wykorzystać jako:
Możesz nawet użyć tego do odwrócenia zmiennych:
Możesz go również użyć do skrócenia
slice()
funkcji.Podstawowe konwersje
ES6 zapewnia znacznie krótszy sposób konwersji formatu Base-2 (binarny) i Base-8 (ósemkowy) na dziesiętny:
+
może być użyty do konwersji ciągu dwójkowego, ósemkowego lub szesnastkowego na liczbę dziesiętną. Można użyć0b
,0o
i0x
na binarny, ósemkowy i hex odpowiednio .:Jeśli używasz tego> 7 razy, użycie
parseInt
i zmiana nazwy będzie krótsza :Teraz
p
można go wykorzystać doparseInt
oszczędzania wielu bajtów na dłuższą metę.źródło
'0x'+v-0
jest jeszcze krótszy, ale może nie działać tak dobrze w niektórych scenariuszach.0767
(ES5) jest krótszy niż0o767
notacja (ES6).0767
jest niestandardowym rozszerzeniem i jest zabronione w trybie ścisłym.0
-fabrykowane literały ósemkowe nigdzie nie idą i są tak samo poprawne jak ekmascript0o
.Używanie szablonów ciągów z funkcjami
Gdy masz funkcję z jednym ciągiem jako argumentami. Możesz pominąć,
()
jeśli nie masz żadnych wyrażeń:źródło
fun`string`
jest taki sam jakfun(["string"])
niefun("string")
. Jest to odpowiednie dla funkcji rzutujących na ciąg znaków, takich jakalert
, ale dla innych może to powodować problemy. Aby uzyskać więcej informacji, zobacz artykuł MDNfun`foo${1}bar${2}baz
jest odpowiednikiem dzwonieniafun(["foo","bar","baz"],1,2)
Zrozumienie tablic (Firefox 30-57)
Uwaga: interpretacje tablic nigdy nie były znormalizowane i stały się przestarzałe w Firefoksie 58. Używaj na własne ryzyko.
Początkowo specyfikacja ECMAScript 7 zawierała wiele nowych funkcji opartych na macierzy. Chociaż większość z nich nie dotarła do wersji finalnej, obsługa Firefoksa (ed) prawdopodobnie największa z tych funkcji: wymyślna nowa składnia, która może zastąpić
.filter
i.map
zefor(a of b)
składnią. Oto przykład:Jak widać, dwie linie nie różnią się od siebie tak bardzo, że druga nie zawiera nieporęcznych słów kluczowych i funkcji strzałek. Ale to tylko uwzględnia zamówienie
.filter().map()
; co się stanie, jeśli.map().filter()
zamiast tego? To zależy od sytuacji:Albo co jeśli chcesz albo
.map
albo.filter
? Cóż, zwykle okazuje się mniej OK:Więc moja rada jest użycie składni dla tablic gdziekolwiek byś zwykle używają
.map
i.filter
, ale nie tylko jedną lub drugą stronę.Rozumienie ciągów
Zaletą w zrozumieniu ES7 jest to, że w przeciwieństwie do funkcji specyficznych dla tablicy, takich jak
.map
i.filter
, można ich używać na dowolnym obiekcie iterowalnym, nie tylko tablicach. Jest to szczególnie przydatne, gdy mamy do czynienia z łańcuchami. Na przykład, jeśli chcesz uruchomić każdy znakc
w ciąguc.charCodeAt()
:To dwa bajty zapisane na dość małą skalę. A co jeśli chcesz filtrować określone znaki w ciągu? Na przykład ten zawiera tylko wielkie litery:
Hmm, to nie jest krótsze. Ale jeśli połączymy dwa:
Wow, zapisano całe 10 bajtów!
Kolejną zaletą ciągów znaków jest to, że ciągi zakodowane na stałe oszczędzają dodatkowy bajt, ponieważ można pominąć spację po
of
:Indeksowanie
Zrozumienie tablic utrudnia uzyskanie bieżącego indeksu w ciągu / tablicy, ale można to zrobić:
Najważniejsze, aby zachować ostrożność, aby upewnić się, że indeks jest zwiększany za każdym razem, a nie tylko wtedy, gdy spełniony jest warunek.
Zrozumienie generatora
Zrozumienia generatora mają zasadniczo tę samą składnię, co zeznania tablicowe; wystarczy zastąpić nawiasy klamrowe:
Tworzy to generator, który działa w taki sam sposób jak tablica, ale to historia na inną odpowiedź.
Podsumowanie
Zasadniczo, chociaż rozumienie jest zwykle krótsze niż
.map().filter()
wszystko, wszystko sprowadza się do specyfiki sytuacji. Najlepiej jest wypróbować to na dwa sposoby i sprawdzić, które z nich działa lepiej.PS Nie wahaj się zasugerować inną wskazówkę dotyczącą zrozumienia lub sposób na poprawienie tej odpowiedzi!
źródło
(x,y)=>[...Array(y-x)].map(a=>x++)
x=>[...Array(x).keys()]
n=>[for(x of Array(n).keys())if(/1/.test(x))x]
(zapisuje 7 bajtów)Wyrażenia funkcyjne w ES6 używają notacji ze strzałką i pomaga dużo zaoszczędzić bajty w porównaniu z wersją ES5:
Jeśli twoja funkcja ma tylko jeden parametr, możesz pominąć nawiasy, aby zapisać dwa bajty:
Jeśli twoja funkcja w ogóle nie ma parametrów, zadeklaruj ją tak, jakby miała jeden, aby zapisać jeden bajt:
Uwaga: funkcje strzałek nie są dokładnie takie same jak
function () {}
. Zasadythis
są różne (i lepiej IMO). Zobacz dokumentyźródło
this
itp.f=(...x)=>x
Miałbyś tof(1,2,3) => [1,2,3]
.(x,y)=>...
, możesz zapisać bajt z curry , zastępując gox=>y=>...
Używanie
eval
funkcji strzałek z wieloma instrukcjami ireturn
Jedna z bardziej absurdalnych sztuczek, na które natknąłem się ...
Wyobraź sobie prostą funkcję strzałki, która wymaga wielu instrukcji i a
return
.Prosta funkcja akceptująca pojedynczy parametr
a
, który iteruje po wszystkich liczbach całkowitych[0, a)
, i umieszcza je na końcuo
zwracanego ciągu wyjściowego . Na przykład wywołanie tego z4
parametrem dałoby wynik0123
.Zauważ, że ta funkcja strzałki musiała być owinięta w nawiasy klamrowe
{}
i miećreturn o
na końcu znak .Ta pierwsza próba waży 39 bajtów .
Nieźle, ale za pomocą
eval
możemy to poprawić.Ta funkcja usunęła nawiasy klamrowe i instrukcję return, zawijając kod w
eval
i po prostu zmieniając ostatnią instrukcję weval
ocenęo
. Powodujeeval
to zwrócenieo
, co z kolei powoduje zwrócenie funkcjio
, ponieważ jest to teraz pojedyncza instrukcja.Ta ulepszona próba waży 38 bajtów , oszczędzając jeden bajt od oryginału.
Ale czekaj, jest więcej! Instrukcje ewaluacyjne zwracają to, do czego ostatnio oceniane. W tym przypadku
o+=i
ocenia sięo
, więc nie potrzebujemy;o
! (Dzięki, edc65!)Ta ostatnia próba waży tylko 36 bajtów - 3 bajty oszczędności w stosunku do oryginału!
Technikę tę można rozszerzyć na każdy ogólny przypadek, w którym funkcja strzałki musi zwrócić wartość i mieć wiele instrukcji (których nie można połączyć innymi metodami)
staje się
zapisywanie bajtu.
Jeśli
statement2
oceni tov
, może to byćoszczędzając w sumie 3 bajty.
źródło
;o
- spróbuj:a=>eval('for(o="",i=0;i<a;i++)o+=i')
Preferuj ciąg nowych linii szablonu zamiast „\ n”
Zacznie się to opłacać nawet w jednym nowym znaku wiersza w kodzie. Jednym przypadkiem użycia może być:
(16 bajtów)
(15 bajtów)
Aktualizacja: Możesz nawet zostawić nawiasy klamrowe z powodu oznakowanych ciągów szablonów (dzięki, edc65!):
(13 bajtów)
źródło
Tablice wypełniania - wartości statyczne i zakresy dynamiczne
Pierwotnie pozostawiłem je jako komentarze pod zrozumieniem, ale ponieważ ten post był przede wszystkim poświęcony zrozumieniu, pomyślałem, że dobrze byłoby nadać temu swoje miejsce.
ES6 dał nam możliwość wypełniania tablic wartościami statycznymi bez użycia pętli:
Oba zwracają tablicę o długości x, wypełnioną wartością 0.
Jeśli jednak chcesz wypełnić tablice wartościami dynamicznymi (np. Zakres od 0 ... x), wynik jest nieco dłuższy (choć nadal krótszy niż w starym przypadku):
Oba zwracają tablicę o długości x, zaczynając od wartości 0 i kończąc na x-1.
Powodem tego
.fill()
jest to, że po prostu inicjowanie tablicy nie pozwoli jej zmapować. To znaczy, że wykonaniex=>Array(x).map((a,i)=>i)
zwróci pustą tablicę. Możesz także ominąć potrzebę wypełniania (a tym samym jeszcze go skrócić), używając operatora rozrzucania w następujący sposób:Za pomocą operatora i
.keys()
funkcji rozrzucania możesz teraz utworzyć krótki zakres 0 ... x:Jeśli chcesz mieć zakres niestandardowy od x ... y lub całkowicie zakres specjalistyczny (taki jak liczby parzyste), możesz się go pozbyć
.keys()
i po prostu użyć.map()
, lub użyć.filter()
, za pomocą operatora rozkładania:źródło
x=>Array(x).fill(i=0).map(a=>i++)
Nie jestem też pewien, czy 0 w.fill(0)
jest konieczne. Próbowałeś tego bez?a=>a%2-1
działa dobrze, podobnie jaka=>a%2<1
.[...Array(x)]
działa równie dobrzeArray(x).fill()
i jest o 2 bajty krótsza.x=>[...Array(x)].map((a,i)=>i)
1/4
przykład byłby krótszy na piśmie[0,0,0,0]
, a 2) funkcje łańcuchowe są specyficzne dla implementacji, więc nie zwrócą wiarygodnej długości (Map
32 bajty w Chrome, ale 36 bajtów w Firefox).Zwracanie wartości w funkcjach strzałek
Powszechnie wiadomo, że jeśli pojedyncza instrukcja występuje po deklaracji funkcji strzałki, zwraca wynik tej instrukcji:
-7 bajtów
Jeśli to możliwe, połącz wiele instrukcji w jedną. Najłatwiej to zrobić, otaczając instrukcje nawiasami i oddzielając je przecinkami:
-8 bajtów
Ale jeśli są tylko dwie instrukcje, zwykle jest możliwe (i krótsze) połączenie ich z
&&
lub||
:-9 bajtów
Wreszcie, jeśli korzystasz z mapy (lub podobnej) i chcesz zwrócić liczbę, a masz gwarancję, że mapa nigdy nie zwróci tablicy o długości 1 z liczbą, możesz zwrócić liczbę za pomocą
|
:źródło
Losowe włamania do szablonów
Ta funkcja riffuje dwa ciągi (tzn. Zamienia się
"abc","de"
w"adbec"
):Pamiętaj, że działa to tylko wtedy, gdy
x
jest dłuższy niży
. Jak to działa, pytasz?String.raw
jest zaprojektowany jako tag szablonu, taki jak:To w zasadzie wymaga
String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y)
, choć nie jest to takie proste. Tablica szablonów ma również specjalnąraw
właściwość, która jest w zasadzie kopią tablicy, ale z nieprzetworzonymi łańcuchami.String.raw(x, ...args)
w zasadzie wracax.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...
i tak dalej, ażx
zabraknie przedmiotów.Teraz, gdy wiemy, jak
String.raw
działa, możemy go wykorzystać na naszą korzyść:Oczywiście ten ostatni
f=(x,y)=>x.split``.join(y)
jest o wiele krótszy, ale masz pomysł.Oto kilka riffling funkcje, które również działają jeśli
x
iy
są równej długości:Możesz dowiedzieć się więcej
String.raw
na temat MDN .źródło
Jak grać w golfa z rekurencją
Rekurencja, choć nie najszybsza, jest często najkrótsza. Zasadniczo rekurencja jest najkrótsza, jeśli rozwiązanie można uprościć do rozwiązania mniejszej części wyzwania, szczególnie jeśli dane wejściowe to liczba lub ciąg znaków. Na przykład, jeśli
f("abcd")
można to obliczyć na podstawie"a"
if("bcd")
, zwykle najlepiej jest użyć rekurencji.Weźmy na przykład silnię:
W tym przykładzie rekurencja jest oczywiście znacznie krótsza niż jakakolwiek inna opcja.
Co powiesz na sumę znaków:
Ten jest trudniejszy, ale widzimy, że przy prawidłowej implementacji rekursja oszczędza 4 bajty
.map
.Teraz spójrzmy na różne typy rekurencji:
Rekurencja wstępna
Zazwyczaj jest to najkrótszy typ rekurencji. Wejście jest podzielona na dwie części
a
ib
, a funkcja oblicza coś za
if(b)
. Wracając do naszego silnego przykładu:W tym przypadku,
a
to n ,b
jest n-1 , a wartość wynosia*f(b)
.Ważna uwaga: wszystkie funkcje rekurencyjne muszą mieć sposób na zatrzymanie rekurencji, gdy dane wejściowe są wystarczająco małe. W funkcji silniakowej jest to kontrolowane za pomocą
n? :1
, tzn. Jeśli wejście ma wartość 0 , zwraca 1 bezf
ponownego wywoływania .Po rekursji
Rekurencja jest podobna do rekurencji, ale nieco inna. Wejście jest podzielona na dwie części
a
ib
, a funkcja oblicza coś za
, a następnie zwracaf(b,a)
. Drugi argument zwykle ma wartość domyślną (tjf(a,b=1)
.).Rekurencja wstępna jest dobra, gdy trzeba zrobić coś specjalnego z końcowym rezultatem. Na przykład, jeśli chcesz silnię liczby plus 1:
Jednak nawet wtedy post- nie zawsze jest krótszy niż użycie rekursji wstępnej w ramach innej funkcji:
Więc kiedy jest krótszy? W tym przykładzie można zauważyć, że rekursja po rekurencji wymaga nawiasów wokół argumentów funkcji, a rekursja wstępna nie. Zasadniczo, jeśli oba rozwiązania wymagają nawiasów wokół argumentów, rekursja po rekurencji jest o około 2 bajty krótsza:
(programy pochodzą z tej odpowiedzi )
Jak znaleźć najkrótsze rozwiązanie
Zwykle jedynym sposobem na znalezienie najkrótszej metody jest wypróbowanie wszystkich. To zawiera:
.map
(dla ciągów albo[...s].map
albos.replace
; dla liczb możesz utworzyć zakres )A to tylko najczęstsze rozwiązania; najlepszym rozwiązaniem może być ich połączenie, a nawet coś zupełnie innego . Najlepszym sposobem na znalezienie najkrótszego rozwiązania jest wypróbowanie wszystkiego .
źródło
Krótsze sposoby na zrobienie tego
.replace
Jeśli chcesz zastąpić wszystkie wystąpienia jednego dokładnego podłańcucha innym ciągiem, oczywistym sposobem byłoby:
Możesz jednak zrobić 1 bajt krótszy:
Pamiętaj, że nie jest to już krótsze, jeśli chcesz korzystać z funkcji wyrażenia regularnego oprócz
g
flagi. Jeśli jednak zastępujesz wszystkie wystąpienia zmiennej, zwykle jest ona znacznie krótsza:Czasami będziesz chciał zamapować każdy znak w ciągu, zastępując każdy inny czymś innym. Często robię to:
Jednak
.replace
prawie zawsze jest krótszy:Teraz, jeśli chcesz zmapować każdy znak w ciągu, ale nie przejmujesz się wynikowym ciągiem,
.map
zwykle lepiej, ponieważ możesz pozbyć się.join``
:źródło
/\w/g
), to użycie zamiany będzie znacznie lepsze niż w tej wersji demonstracyjnej .Pisanie literałów RegEx za pomocą
eval
Konstruktor wyrażeń regularnych może być bardzo nieporęczny z powodu swojej długiej nazwy. Zamiast tego napisz literał z eval i backticks:
Jeśli zmienna
i
jest równafoo
, wygeneruje:Jest to równe:
Możesz także użyć,
String.raw
aby uniknąć konieczności wielokrotnego unikania ukośników odwrotnych\
Spowoduje to wygenerowanie:
Co jest równe:
Pamiętać!
String.raw
zajmuje dużo bajtów i jeśli nie masz przynajmniej dziewięciu odwrotnych ukośników,String.raw
będzie dłuższy.źródło
new
tam, więc użycie konstruktora jest w rzeczywistości krótsze w drugim przykładzie.forEach
vsfor
pętleZawsze wolę
.map
dowolną z pętli for. Łatwe, natychmiastowe oszczędności.źródło
Używanie niezainicjowanych liczników w rekurencji
Uwaga : Ściśle mówiąc, nie dotyczy to ES6. Jednak bardziej sensowne jest używanie i nadużywanie rekurencji w ES6, ze względu na zwięzły charakter funkcji strzałek.
Często spotyka się funkcję rekurencyjną, która używa licznika
k
początkowo ustawionego na zero i zwiększanego przy każdej iteracji:W pewnych okolicznościach można pominąć inicjalizację takiego licznika i zastąpić
k+1
go-~k
:Ta sztuczka zwykle oszczędza 2 bajty .
Dlaczego i kiedy to działa?
Formuła, która to umożliwia, to
~undefined === -1
. Tak więc podczas pierwszej iteracji-~k
zostanie oceniony jako1
. W następnych iteracjach-~k
jest zasadniczo równoważny temu,-(-k-1)
co jest równek+1
, co najmniej dla liczb całkowitych z zakresu [0… 2 31 -1].Musisz jednak upewnić się, że
k = undefined
wykonanie pierwszej iteracji nie zakłóci działania funkcji. Należy szczególnie pamiętać, że większość operacji arytmetycznychundefined
przyniesie to skutekNaN
.Przykład 1
Biorąc pod uwagę dodatnią liczbę całkowitą
n
, ta funkcja szuka najmniejszej liczby całkowitejk
, która się nie dzielin
:Można go skrócić do:
Działa
n % undefined
toNaN
, ponieważ jest , co jest fałszem. To oczekiwany wynik przy pierwszej iteracji.[Link do oryginalnej odpowiedzi]
Przykład nr 2
Biorąc pod uwagę dodatnią liczbę całkowitą
n
, ta funkcja szuka liczby całkowitejp
takiej, że(3**p) - 1 == n
:Można go skrócić do:
Działa
p
to, ponieważ w ogóle nie jest używane przy pierwszej iteracji (n<k
fałsz).[Link do oryginalnej odpowiedzi]
źródło
Funkcje ES6
Matematyka
Math.cbrt(x)
zapisuje znaki niżMath.pow(x,1/3)
.3 znaki zapisane
Math.hypot(...args)
przydaje się, gdy potrzebujesz pierwiastka kwadratowego z sumy kwadratów argumentów. Wykonanie tego kodu ES5 jest o wiele trudniejsze niż użycie wbudowanego.Funkcja
Math.trunc(x)
nie byłaby pomocna, ponieważx|0
jest krótsza. (Dzięki Mwr247!)Istnieje wiele właściwości, które wymagają dużo kodu w ES5, ale łatwiej w ES6:
Math.acosh
,asinh
,atanh
,cosh
,sinh
,tanh
. Oblicza hiperboliczny odpowiednik funkcji trygonometrycznych.Math.clz32
. Może być to możliwe w ES5, ale teraz jest łatwiejsze. Zlicza zera wiodące w 32-bitowej reprezentacji liczby.Istnieje wiele więcej, więc mam zamiar wymienić tylko kilka:
Math.sign
,Math.fround
,Math.imul
,Math.log10
,Math.log2
,Math.log1p
.źródło
Math.trunc(x)
jest czterokrotnie dłuższy niżx|0
.Math.hypot(a,b) => Math.sqrt(a*a+b*b)
(3 bajty dłużej; staje się jeszcze dłuższy z większą liczbą argumentów),Math.sign(a) => (a>0)-(a<0)
(1 bajt krótszy, ale w niektórych przypadkach potrzebuje otaczających nawiasów; może nie współpracowaćNaN
)Optymalizacja małych stałych zakresów dla
map()
Kontekst
map()
for
można zastąpić:
lub częściej:
Array(N)
Uwaga : Długość kodu wywołania zwrotnego
F(i)
nie jest liczona.Optymalizacje bez licznika
Uwaga : Długość kodu wywołania zwrotnego
F()
nie jest liczona.źródło
2**26
być2**29
?.keys()
, nie potrzebujesz lambda:[...Array(10).keys()].map(do_something_with)
Zadania związane z restrukturyzacją
ES6 wprowadza nową składnię przypisań destrukcyjnych, tj. Wycinanie wartości na części i przypisywanie każdej części do innej zmiennej. Oto kilka przykładów:
Ciągi i tablice
Obiekty
Te przypisania można również wykorzystać w parametrach funkcji:
źródło
Jeszcze inny sposób na uniknięcie
return
Wiesz, że powinieneś używać eval do funkcji strzałek z wieloma instrukcjami i zwrotem . W niektórych nietypowych przypadkach możesz zaoszczędzić więcej za pomocą wewnętrznej podfunkcji.
Mówię niezwykłe, ponieważ
Zwrócony wynik nie może być ostatnim wyrażeniem ocenianym w pętli
Przed pętlą muszą być (co najmniej) 2 różne inicjalizacje
W takim przypadku możesz użyć wewnętrznej podfunkcji bez powrotu, mając jedną z wartości początkowych przekazaną jako parametr.
Przykład Znajdź odwrotność sumy funkcji exp dla wartości w zakresie od a do b.
Długa droga - 55 bajtów
Z eval - 54 bajty
Z funkcją wewnętrzną - 53 bajty
Zauważ, że bez wymogu dolnej granicy zakresu
a
, mogę scalić inicjalizacje i i r, a wersja eval jest krótsza.źródło
a
(i,b)=>{for(r=0;i<=b;i++)r+=Math.exp(i);return 1/r}
return a/r
byłby lepszy przykład(a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")
w tym przypadku(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
Używanie składni curry do funkcji diadycznych i rekurencyjnych
Funkcje dyadyczne
Ilekroć funkcja pobiera dokładnie dwa argumenty bez wartości domyślnych, użycie składni curry pozwala zaoszczędzić jeden bajt.
Przed
Zadzwoniłem z
f(a,b)
Po
Zadzwoniłem z
f(a)(b)
Uwaga : ten post w Meta potwierdza poprawność tej składni.
Funkcje rekurencyjne
Użycie składni curry może również zaoszczędzić niektóre bajty, gdy funkcja rekurencyjna pobiera kilka argumentów, ale wystarczy zaktualizować niektóre z nich między każdą iteracją.
Przykład
Poniższa funkcja oblicza sumę wszystkich liczb całkowitych w zakresie
[a,b]
:Ponieważ
a
pozostaje niezmieniony podczas całego procesu, możemy zapisać 3 bajty, używając:Uwaga : Jak zauważył Neil w komentarzach, fakt, że argument nie jest jawnie przekazywany funkcji rekurencyjnej, nie oznacza, że należy ją uznać za niezmienną. W razie potrzeby, możemy modyfikować
a
w kodzie funkcji za++
,a--
czy cokolwiek podobnego składnia.źródło
a=>F=b=>a>b?0:a+++F(b)
, modyfikująca
dla każdego wywołania rekurencyjnego. To nie pomaga w takim przypadku, ale może zaoszczędzić bajty w przypadkach z większą liczbą argumentów.Funkcja testowania pierwotności
Zwraca następującą funkcję 28-bajtową
true
dla liczb pierwszych ifalse
dla liczb niepierwszych:Można to łatwo zmodyfikować, aby obliczyć inne rzeczy. Na przykład ta 39-bajtowa funkcja liczy liczbę liczb pierwszych mniejszą lub równą liczbie:
Jeśli masz już zmienną
n
, którą chcesz sprawdzić pod kątem pierwotności, funkcję pierwotności można nieco uprościć:Jak to działa
Uwaga: nie powiedzie się to z powodu błędu „zbyt dużej rekurencji”, gdy zostanie wywołany z odpowiednio dużym wejściem, takim jak 12345. Można to obejść za pomocą pętli:
źródło
x==1
prawdopodobnie może byćx<2
dla oszczędności.1
lub0
(ponieważx
byłoby odpowiednio0
lub-1
)!~-x
dla -0 bajtów.Array#concat()
i operator spreaduZależy to w dużej mierze od sytuacji.
Łączenie wielu tablic.
Preferuj funkcję concat, chyba że klonujesz.
Zapisano 0 bajtów
3 bajty zmarnowane
Zapisano 3 bajty
Zapisano 6 bajtów
Preferuj użycie już istniejącej tablicy do
Array#concat()
.Łatwe 4 bajty zapisane
źródło
Zwraca wynik pośredni
Wiesz, że za pomocą operatora przecinka możesz wykonać sekwencję wyrażeń zwracających ostatnią wartość. Ale nadużywając literalnej składni tablicowej, możesz zwrócić dowolną wartość pośrednią. Jest to przydatne na przykład w .map ().
źródło
.join('')
może to być.join``
Ustaw wartości domyślne parametrów funkcji
Ten jest naprawdę przydatny ...
Pamiętaj jednak, aby zrozumieć, że coś takiego
_=>_||'asdf'
jest krótsze, gdy przekazujesz tylko jeden (użyteczny) argument do funkcji.źródło
_=>_||'asdf'
jest zwykle krótsze w większości przypadków"asdf"
wartość wejściową""
(pusty ciąg).undefined
, nawet jeśli jawnie przekażesz tę wartość. Na przykład[...Array(n)].map((a,b,c)=>b)
zawsze przechodziundefined
naa
, dlatego możesz podać dla niego wartość domyślną (choć nie w kategoriachb
).Użyj
eval
zamiast nawiasów klamrowych dla funkcji strzałekFunkcje strzałek są niesamowite. Przyjmują formę
x=>y
, gdziex
jest argumentem iy
wartością zwracaną. Jeśli jednak chcesz użyć struktury kontrolnej, takiej jak np.while
, Musisz wstawić nawiasy klamrowe, np=>{while(){};return}
. Możemy jednak obejść ten problem; na szczęścieeval
funkcja pobiera ciąg, ocenia ten ciąg jako kod JS i zwraca ostatnio ocenione wyrażenie . Na przykład porównaj te dwa:Możemy użyć rozszerzenia tej koncepcji, aby jeszcze bardziej skrócić nasz kod: w oczach
eval
struktur kontrolnych zwraca również ostatnio ocenione wyrażenie. Na przykład:źródło
Gra logiczna w golfa w ES6
„GLOE (S6)”
Ogólna logika
Załóżmy, że zbudowałeś oświadczenia
s
it
. Sprawdź, czy możesz użyć jednego z następujących zamienników:(Mogą nie działać, jeśli zamówienie jest źle, to znaczy
+
i*
mają pierwszeństwo zamówienia mniejszą niż||
i&&
robić).Oto kilka przydatnych wyrażeń logicznych:
s
albot
jest prawdą / XOR:s^t
s
it
mają tę samą wartość prawdy:!s^t
lubs==t
Logika tablicowa
Wszyscy członkowie
a
spełniają warunekp
:Co najmniej jeden członek
a
spełnia warunekp
:Brak członków
a
spełniających warunekp
:!a.some(p)
.Element
e
istnieje w tablicya
:Element
e
ma nie istnieć w tablicya
:źródło
&&
i||
jakx?y:x
ix?x:y
, odpowiednio. Ale widzę, jak byłoby to użyteczne w programach opartych na logice. Jedyny problem+
to, że na przykład3
i-3
to zarówno truthy, ale3+-3
nie jest.-
może również działać, jeślis != t
.a.filter(t=>t==e).length==a.length
jest nieprawidłowe. Powinno być!a.filter(t=>t==e).length
Skróć powtarzane wywołania funkcji
Jeśli powtórzono wywołania funkcji o długiej nazwie, takiej jak manipulacja na kanwie:
Tradycyjnym sposobem skrócenia tego byłoby alias nazwy funkcji:
Jeśli masz wystarczającą liczbę połączeń, lepszym sposobem jest utworzenie funkcji, która wykona zadanie za Ciebie:
Jeśli większość wywołań funkcji jest połączonych w łańcuch, możesz sprawić, że funkcja sama się zwróci, co pozwoli ci odciąć dwa bajty z każdego kolejnego wywołania:
Przykładowe użycie: 1 , 2
źródło
(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
c.lineTo
naturalnie się nie zwraca)Operator powiązania
::
Za pomocą operatora powiązania można skrócić bajty w stosunku do powtarzających się funkcji:
Dodatkowo, jeśli chcesz użyć funkcji z innymi
this
np .:źródło
Unikanie przecinków podczas przechowywania dużej ilości danych
Jeśli masz dużo danych (tj. Indeksów, znaków itp.), Które musisz przechowywać w tablicy, lepiej zostawić wszystkie przecinki. Działa to najlepiej, jeśli każdy kawałek danych ma tę samą długość łańcucha, przy czym 1 jest oczywiście optymalny.
43 bajtów (linia bazowa)
34 bajty (bez przecinków)
Jeśli chcesz zmienić dostęp do tablicy , możesz to jeszcze bardziej zmniejszyć, przechowując takie same wartości:
27 bajtów (te same dane, zmienia tylko dostęp do tablicy)
źródło