ECMAScript 6 ve daha üstündeki Golf için ipuçları


88

Bu, "<...> 'de golf oynama ile ilgili ipuçları" ile benzerdir, ancak özellikle ECMAScript 6 ve daha üstü olan JavaScript'teki yeni özellikleri hedef alır.

JavaScript doğal olarak çok ayrıntılı bir dildir function(){}, .forEach()vb dizinin, dizi dizi benzeri nesne, vb dize dönüştürme süper Bloats ve Golf için sağlıklı değildir.

Öte yandan, ES6 +, bazı süper kullanışlı özelliklere ve daha az kaplama alanına sahiptir. x=>y, [...x]vb. örneklerden bazıları.

Lütfen kodunuzdaki fazladan birkaç bayttan kurtulmanıza yardımcı olabilecek bazı püf noktaları gönderin.

NOT: ES5 için püf noktaları , JavaScript'te golf oynamak için ipuçları bölümünde zaten mevcuttur ; Bu konuya verilen cevaplar sadece ES6 ve diğer ES sürümlerinde mevcut olan püf noktalarına odaklanmalıdır.

Ancak, bu konu ES5 özelliklerini kullanarak şu anda golf oynayanlar için de geçerlidir. Yanıtlar ayrıca, ES6 özelliklerini ES5 kodlama stillerine göre anlamalarına ve eşlemelerine yardımcı olacak ipuçları da içerebilir.

Yanıtlar:


42

Yayılma operatörü ...

Spread operatörü, bir dizi değerini virgülle ayrılmış bir listeye dönüştürür.

1. olayı kullanın:

Bir işlevin listeyi beklediği bir diziyi doğrudan kullanma

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

2. olayı kullanın:

Yinelenebilir bir diziden hazır bilgi dizisi oluşturma (genellikle bir dize)

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

3. olayı kullanın:

Bir fonksiyon için değişken sayıda argüman bildir

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

Mozilla doc bakın


3
Şimdi burada bir oyum var. Açıkçası biri bu ipucunda çok yanlış bir şey olduğunu belirtti, yorum yapmak ve açıklamaktan çok utangaç olmak ...
edc65

Tamam görünüyor. Belki noktalı virgül eksikliği oldu? ;) (btw, Ruby'deki uyarılar gibi dinlenme parametreleri olarak da kullanabilirsiniz)
gcampbell

İşlev imzalarında da bir kullanım durumu olduğunu ekleyebilirsin :)
Felix Dombek

Misclick düşürmek istemedi
Stan Strum

@StanStrum bu olur. Sonunda oyunuzu değiştirebilmeniz için bu yazı üzerinde küçük bir güncelleme yapacağım (ya da zaten yaptınız mı?)
edc65

21

Katıldığımdan beri burada numaralar öğrenildi.

Ana programlama dilim JS ve çoğunlukla ES6. Bu siteye bir hafta önce katıldığımdan beri, arkadaşlarımdan birçok faydalı numara öğrendim. Bunlardan bazılarını burada birleştiriyorum. Topluluğa verilen tüm krediler.

Ok fonksiyonları ve döngüler

Ok fonksiyonlarının çok fazla bayt kurtardığını hepimiz biliyoruz.

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

Fakat aklınızda bulundurmanız gereken birkaç şey var.

  • Kullanarak kulüp birden ifadelerine deneyin ,yani (a=b,a.map(d))döndürülen değeri, son ifadedir, İşte -a.map(d)
  • parçanız do somethingbirden fazla ifade ise, çevreleyen {}parantezleri eklemeniz gerekir .
  • Çevreleyen {}parantez varsa, açık bir return ifadesi eklemeniz gerekir.

İlgili döngüler olduğunda yukarıdakiler çoğu zaman geçerlidir. Yani şöyle bir şey:

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

Burada iade nedeniyle en az 9 karakter harcıyorum. Bu optimize edilebilir.

  • Döngüler için kaçınmaya çalışın. Kullanın .mapveya .everyveya .someonun yerine. Eşlediğiniz diziyi değiştirmek isterseniz, başarısız olacağını unutmayın.
  • Ana ok işlevini tek bir ifade olarak dönüştürerek, döngüyü kapatma ok işlevine sarın.

Böylece yukarıdakiler olur:

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]

kaldırılan karakterler: {}return

eklenen karakterler: (){}>|

Değişkeni doğru şekilde yerleştiren kapatma yöntemini nasıl çağırdığımı nve daha sonra kapatma yönteminin hiçbir şeyi döndürmediğinden (örneğin, geri undefineddöndüğümden), bitsel olarak ya da ndışa döndüğümden ve dizinin tümünü dış ok işlevinin tek bir ifadesinde döndürdüğüme dikkat edin.u

Virgül ve noktalı virgül

Ne de olsa onlardan kaçın,

Değişkenleri bir döngüde veya önceki bölümde belirtildiği gibi ,, tek ifadeli ok işlevlerine sahip olmak üzere ayrılmış ifadeleri kullanarak bildiriyorsanız, bunlardan kaçınmak ,veya ;son birkaç bayttan kurtulmak için oldukça şık hileler kullanabilirsiniz .

Bu kodu göz önünde bulundurun:

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

Burada birçok değişkeni başlatmak için birçok yöntem çağırıyorum. Her başlatma a ,veya kullanıyor ;. Bu, şu şekilde yeniden yazılabilir:

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

Yöntemin kendisine iletilen değişkeni rahatsız etmediğini ve 3 byte'ı tıraş etmek için bu gerçeği nasıl kullandığımı not edin.

Çeşitli

.search onun yerine .indexOf

Her ikisi de aynı sonucu verir, ancak searchdaha kısadır. Arama Düzenli İfade beklemesine rağmen, akıllıca kullanın.

Şablon Dizeleri

Bunlar, belirli koşullara göre bir veya daha fazla ip parçasını birleştirmeniz gerektiğinde süper kullanışlıdır.

JS'de bir çıktı vermek için aşağıdaki örneği kullanın

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

vs.

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

İki backquotes (`) içindeki bir dize olan bir şablon dizgede, içindeki herhangi bir şey ${ }bir kod olarak değerlendirilir ve sonuçtaki cevabı dizeye eklemek için değerlendirilir.

Daha sonra birkaç numara daha göndereceğim. Mutlu golf!


1
.Arama daha kısa, mümkün olduğunda kullanın! ama .indexOf ile aynı değildir. .search regexpbir dize değil a istiyor . Deneyin'abc'.search('.')
edc65

@ edc65 Güncelleme!
Doktor

Orijinal diziyi örnek yöntemlerle değiştirebilirsiniz. İkincisi, geçerli dizin ve üçüncüsü yinelenen dizidir.
Isiah Meadows,

8
"Siteye bir hafta geri katıldı" - 21.4k temsilcisi ...
GamrCorps

2
Bunun yanı sıra .map, özyineleme bazen bir fordöngüyü bir ifadeye dönüştürmenize yardımcı olabilecek başka bir tekniktir .
Neil,

20

Özellik kısayollarını kullanma

Özellik kısaltmaları, değişkenleri bir dizinin değerlerine ayarlamanızı sağlar:

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

Bu ayrıca şu şekilde de kullanılabilir:

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

Bunu değişkenleri tersine çevirmek için bile kullanabilirsiniz:

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

Bunu, slice()işlevleri kısaltmak için de kullanabilirsiniz .

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

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

Temel dönüşümler

ES6, Base-2 (ikili) ve Base-8 (sekizli) formlarını ondalık basamağa dönüştürmek için çok daha kısa bir yol sağlar:

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

+ikili, sekizli veya onaltılı bir dize ondalık sayıya dönüştürmek için kullanılabilir. Sen kullanabilirsiniz 0b, 0ove 0xsırasıyla ikili, sekizli ve onaltılık için, .:

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

Bunu> 7 kez kullanıyorsanız, kullanması parseIntve yeniden adlandırması daha kısa olacaktır :

(p=parseInt)(v,2)

Şimdi uzun vadede size birçok byte tasarrufu piçin kullanılabilir parseInt.


Temel dönüşüm hilesi güzeldir, ancak dönüşüm sayısının değişmez yerine değişken şeklinde olması daha muhtemeldir, bu durumda daha uzun olur.
Doktor,

1
'0x'+v-0daha kısadır, ancak bazı senaryolarda da çalışmayabilir.
ETHProductions

1
Bu arada, 0767(ES5), 0o767(ES6) notasyonundan daha kısadır .
Camilo Martin

@CamiloMartin 0767standart olmayan bir eklentidir ve katı modda kesinlikle yasaktır.
Oriol

1
@ Oriol katı mod kötü bir meme oldu. Performansa yardımcı olmadı, sizi gerçekten iyi bir kod yazmaya zorlamadı ve hiçbir zaman varsayılan olmadı. 0Önceden belirlenmiş sekizli değişmezler hiçbir yere gitmiyor ve olduğu gibi geçerli bir ecmascript 0o.
Camilo Martin,

19

String şablonlarını fonksiyonlarla kullanma

Argümanlar olarak tek bir dize ile bir işleve sahip olduğunuzda. İfadeniz ()yoksa , atlayabilirsiniz :

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

9
Uyarı, bu aslında bir dizi geçer. fun`string` aynı fun(["string"])değil fun("string"). Bu, dizgeye benzer işlevler için iyidir alert, ancak diğerleri için bu sorunlara neden olabilir. Daha fazla bilgi için MDN makalesine bakın
Cyoce

5
Hızlı referans olayı: fun`foo${1}bar${2}bazaramaya eşdeğerfun(["foo","bar","baz"],1,2)
Cyoce

14

Dizi Anlamaları (Firefox 30-57)

Not: dizi kavrama asla standartlaştırılmadı ve Firefox 58 ile eski hale getirildi. Kendi sorumluluğunuzda kullanın.


Başlangıçta, ECMAScript 7 spec bir dizi yeni dizi tabanlı özellik içeriyordu. Bunların çoğu kesinleşmiş versiyonda olmasa da, Firefox desteği (ed) muhtemelen şu özelliklerin en büyüğüdür: yerine geçebilecek .filterve sözdizimi .mapile olabilecek yeni for(a of b)sözdizimi. İşte bir örnek:

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

Gördüğünüz gibi, iki satır, hantal anahtar kelimeleri ve ok işlevlerini içermeyen ikinciden farklı değildir. Ancak bu sadece siparişi açıklar .filter().map(); .map().filter()onun yerine varsa ne olacak ? Gerçekten duruma bağlı:

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]

Ya da istersen ya .map da .filter ? Genelde daha az olur.

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]

Genellikle kullanacağınız yerde Yani benim tavsiyem dizi comprehensions kullanmaktır .map ve .filter sadece bir ya da diğer fakat.

Dize Kavramaları

ES7 anlamalarıyla ilgili güzel bir şey, .mapve .filterbunlar gibi diziye özgü işlevlerin aksine , yalnızca diziler için değil, yinelenebilir herhangi bir nesnede kullanılabiliyor olmasıdır. Bu, özellikle iplerle uğraşırken kullanışlıdır. Örneğin, her karakteri cbir dizgede çalıştırmak istiyorsanız c.charCodeAt():

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

Bu oldukça küçük bir ölçekte kaydedilen iki bayt. Ve bir dizgede belirli karakterleri filtrelemek istersen? Örneğin, bu sadece büyük harfleri tutar:

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

Hmm, bu daha kısa değil. Ama ikisini birleştirirsek:

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

Vay, 10 bayt kurtardı!

Dizgi anlamaların bir başka avantajı da, kodlanmış dizgilerin fazladan bayt tasarrufu sağlamasıdır, çünkü boşluğu sonra atlayabilirsiniz 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]

indeksleme

Dizi kavrayışları dizgede / dizideki geçerli dizini almayı biraz zorlaştırır, ancak yapılabilir:

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

Dikkat edilmesi gereken en önemli şey, endeksin yalnızca bir koşul sağlandığında değil her seferinde artmış olmasını sağlamaktır .

Jeneratör anlama

Jeneratör anlama, temel olarak dizi anlama ile aynı sözdizimine sahiptir; Sadece parantez ile parantez değiştirin:

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

Bu, bir dizi ile aynı şekilde çalışan bir jeneratör oluşturur , ancak bu başka bir cevabın hikayesidir.

özet

Temel olarak, kavrayışlar genellikle olduğundan daha kısa olmasına rağmen .map().filter(), hepsi durumun özelliklerine bağlı olarak gelir. Her iki yolu da denemek ve hangisinin daha iyi sonuç verdiğini görmek en iyisidir.

PS, anlama ile ilgili başka bir ipucu veya bu cevabı geliştirmenin bir yolunu önermekten çekinmeyin!


İşte birkaç karakter daha kazandıracak aralıklar için bir numara:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
0 ile x arasında bir aralık yapmak için başka bir 11 baytı kesebilirsiniz:x=>[...Array(x).keys()]
Mwr247

Orada anlama için n=>[for(x of Array(n).keys())if(/1/.test(x))x]
sonuncusu

@ Mwr247 Aslında, aralıkların genellikle diğer güzel ES6 özelliklerinde olduğu gibi anlama ile kısa olmadığını görebiliyorum. Bunun yerine dizeler üzerine bir bölüm ekleyeceğim ve aralıkları yönetmenize izin vereceğim.
ETHProductions

Dizi Anlayışlarının kullanımdan kaldırıldığı ve javascript'in tüm son sürümlerinden kaldırıldığı unutulmamalıdır. Konuyla ilgili MDN belgelerine bakın .
Keefer Rourke

13

ES6'daki işlev ifadeleri ok gösterimini kullanır ve ES5 sürümüyle karşılaştırıldığında çok fazla tasarruf baytı sağlar:

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

Eğer fonksiyonunuz sadece bir parametreye sahipse, iki byte kaydetmek için parantezleri atlayabilirsiniz:

f=x=>x+1

Eğer fonksiyonunuzda hiç parametre yoksa, onu bir bayttan tasarruf edecek bir tanesine sahip olduğunu bildirin:

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

Dikkat: Ok fonksiyonları tam olarak aynı değildir function () {}. Kuralları thisfarklı (ve daha iyi IMO). Dokümanlara bakın


2
Ancak golf oynarken, genellikle thisvb umurumda değil .
Doktor

1
Genelde değil, ama bir uyarı, geldiğinde asla bilemeyeceğiniz bir uyarı. Ayrıca lambdaların üretimde bu bağlayıcılığa bağlı bir fonksiyona ihtiyacı olmaması daha yaygındır.
Isiah Meadows,

Ayrıca, tüm argümanlarınızı almak istiyorsanız, "rest" argüman özelliğini kullanabilirsiniz, örneğin, f=(...x)=>x buna sahipti f(1,2,3) => [1,2,3].
Conor O'Brien,

1
İşte bu siteye bir ipucu özgü verilmiştir: Eğer halini alır bir işlevle yanıtlayan eğer (x,y)=>...yapabilirsiniz -işlemden ile byte kaydetmek ile değiştirerekx=>y=>...
Cyoce

12

Kullanılması evalbirden tablolar ve a ile ok fonksiyonları içinreturn

Karşılaştığım en saçma numaralardan biri ...

Birden çok ifadeye ihtiyaç duyan basit bir ok işlevi düşünün ve a return.

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

aİçindeki tüm tamsayıları yineleyen [0, a)ve bunları odöndürülen çıktı dizesinin sonuna bağlayan tek bir parametreyi kabul eden basit bir işlev . Örneğin, 4bunu parametre olarak kullanmak ile çağırmak 0123.

Bu ok işlevinin parantez içine alınması ve sonunda {}bir bit olması gerektiğine dikkat return oedin.

Bu ilk deneme 39 byte ağırlığındadır .

Fena değil, ancak evalbunu kullanarak bunu iyileştirebiliriz.

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

Bu işlev, kodu bir içine sararak evalve sadece evaldeğerlendirme içindeki son ifadeyi yaparak parantezleri ve return deyimini kaldırdı o. Bu neden evaldönmek için osırayla işlev dönmek neden olan oartık tek bir deyim olduğundan,.

Bu geliştirilmiş deneme 38 byte ağırlığında ve orijinalden bir byte tasarruf sağlar.

Ama bekleyin, dahası var! Eval ifadeleri, değerlendirildikleri son ifadeleri ne olursa olsun döndürür. Bu durumda, o+=ideğerlendirir o, bu yüzden ihtiyacımız yok ;o! (Teşekkürler, edc65!)

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

Bu son deneme yalnızca 36 bayt ağırlığında - orijinaline göre 3 baytlık bir tasarruf!


Bu teknik, bir ok işlevinin bir değer döndürmesi ve birden fazla ifadeye sahip olması gereken herhangi bir genel duruma genişletilebilir (diğer yöntemlerle birleştirilemez)

b=>{statement1;statement2;return v}

olur

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

Bir bayt kaydediliyor.

Eğer statement2değerlendirirse v, bu olabilir

b=>eval('statement1;statement2')

toplam 3 bayt kaydediliyor.


1
Bence, sadece isimsiz bir işlev yazmak daha da kısa olabilir
Downgoat

@ vihan evet, bu fonksiyonların her ikisi de 2 bayt kurtarmak için isimsiz yapılabilir. Bir byte tasarruf yine de duruyor.
Jrich,

1
Ama daha da iyisi: eval değerlendirilen son ifadeyi döndürür, bu yüzden gerek yok ;o- deneyin:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65 10:15

4
Ancak şablon dizeleri!
Conor O'Brien

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ Örnek dizgiyi bağlam olarak kullanarak şablon dizgilerin burada nasıl çalışacağını açıklamaya özen gösterin?
WallyWest

10

"\ N" yerine şablon dizgisini yeni satırlar tercih et

Bu, kodunuzdaki tek bir yeni satır karakterinde bile ödemeye başlar. Bir kullanım durumu olabilir:

(16 bayt)

array.join("\n")

(15 bayt)

array.join(`
`)

Güncelleme: Etiketli şablon dizeleri nedeniyle bile telleri bile bırakabilirsiniz (teşekkürler, edc65!):

(13 bayt)

array.join`
`

5
Ama daha da iyisi, parantezden kaçınabilirsiniz. Nedenini görmek için docs ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) okuyun
edc65

Ah doğru. Teşekkürler, ekledim.
Chiru

9

Dizileri Doldurma - Statik Değerler ve Dinamik Aralıklar

Bunları başlangıçta anlamaların altında yorum olarak bıraktım, ancak bu yayın öncelikle anlamalara odaklandığından, ona kendi yerini vermenin iyi olacağını düşündüm.

ES6 bize döngüleri kullanmadan dizileri statik değerlerle doldurma imkanı verdi:

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

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

Her ikisi de 0 değeriyle dolu bir uzunluk x dizisi döndürür.

Ancak, dizileri dinamik değerlerle (örneğin 0 ... x aralığında) doldurmak istiyorsanız, sonuç biraz daha uzundur (yine de eski yoldan daha kısa olmasına rağmen):

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

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

Her ikisi de, 0 değeri ile başlayan ve x-1 ile biten, bir uzunluk uzunluğu dizisi döndürür.

İçeri girmenizin nedeni .fill(), sadece bir diziyi başlatmak, onu eşlemenize izin vermemesidir. Yani x=>Array(x).map((a,i)=>i)boş bir dizi geri dönecek. Spread operatörünü kullanarak aşağıdaki gibi doldurma ihtiyacını da (ve böylece daha da kısaltabilirsiniz) de alabilirsiniz:

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

Spread operatörünü ve .keys()işlevini kullanarak şimdi kısa bir 0 ... x aralığı yapabilirsiniz:

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

X ... y arasında özel bir aralık veya tamamen özel bir aralık (hatta sayılar gibi) istiyorsanız , spread operatörüyle kurtulup .keys()hemen kullanabilirsiniz .map()veya kullanabilirsiniz .filter():

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

İşte ikinci örnek için bir öneri: x=>Array(x).fill(i=0).map(a=>i++)Ayrıca, 0'ın .fill(0)gerekli olduğundan emin değilim . Onsuz denediniz mi?
ETHProductions,

@ETHproductions Sen haklısın, 0 önce harita dolgusunda gerekli olmadığını unuttum. Bu, sizin önerilenden 1 karakter daha kısa olmasını sağlar, bu yüzden böyle tutacağım. Teşekkürler!
Mwr247

Ayrıca, son örnek için olduğu a=>a%2-1gibi iyi çalışıyor a=>a%2<1.
ETHProductions

1
Öğrendiğim yeni bir püf noktası : [...Array(x)]hem iyi hem Array(x).fill()de 2 bayt daha kısa. x=>[...Array(x)].map((a,i)=>i)
ETHProductions

1
@yonatanmn Çok hoş! Yalnızca yorumlar 1 olacaktır; 1/4örnek daha kısa sürede yazılır [0,0,0,0]ve 2) stringized işlevler uygulamaya özeldir, bu nedenle güvenilir bir uzunluk döndürmez ( MapChrome'da 32 bayttır, ancak Firefox'ta 36 bayttır).
Mwr247

9

Ok İşlevlerinde Değerleri Döndürme

Tek bir ifadenin ok işlevi bildirgesini izlemesi durumunda, bu ifadenin sonucunu döndürdüğü yaygın bir bilgidir:

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

-7 bayt

Bu nedenle, mümkün olduğunda, birden çok ifadeyi birde birleştirin. Bu, en kolay ifadeleri parantez içine alarak ve virgüllerle ayırarak yapılır:

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

-8 bayt

Ancak, yalnızca iki ifade varsa, bunları &&veya ile birleştirmek genellikle mümkündür (ve daha kısadır) ||:

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 bayt

Son olarak, haritayı (veya benzerini) kullanıyorsanız ve bir sayı döndürmeniz gerekiyorsa ve haritanın 1 numaralı bir diziyi asla bir sayı ile döndürmeyeceğini garanti ederseniz, sayıyı aşağıdaki şekilde döndürebilirsiniz |:

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

Bu son örnekte, sayının her zaman bir tamsayı olacağından emin olmanız gerekir.
ETHProductions,

8

Rastgele şablon dizgisi kesmek

Bu işlev iki dizgiyi karıştırır (yani, "abc","de"dönüşür "adbec"):

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

Bunun yalnızca xdaha uzun olduğunda çalıştığını unutmayın y. Nasıl çalışıyor soruyorsun? String.rawşöyle bir şablon etiketi olacak şekilde tasarlanmıştır:

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

Bu temelde çağırıyor String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), ama o kadar basit değil. Ayrıca şablon dizisi raw, temel olarak dizinin bir kopyası olan ancak ham dizelerle özel bir özelliğe sahiptir . String.raw(x, ...args)temelde döner x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...ve kadarx öğeler .

Artık nasıl String.rawçalıştığını bildiğimize göre , bunu avantajımız için kullanabiliriz:

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"

Tabii ki, bu sonuncusu, f=(x,y)=>x.split``.join(y)çok daha kısa, ama sen anladın.

Eşit uzunlukta olan xve aynı zamanda çalışan bir çift oluşturma fonksiyonu y:

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

String.raw MDN hakkında daha fazla bilgi edinebilirsiniz .


7

Özyineleme ile golf nasıl

Özyineleme, en hızlı seçenek olmasa da, çoğu zaman en kısa yoldur. Genel olarak, özüm özüm, özümün zorluğun daha küçük bir kısmına sadeleştirilebilmesi durumunda, özellikle girdi bir sayı veya bir dize olduğunda en kısadır. Örneğin, eğer f("abcd")hesaplanabilir "a"ve f("bcd")bu özyinelemeyi kullanmak genellikle en iyi,.

Örneğin, faktoring atın:

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

Bu örnekte, özyineleme açıkça, diğer seçeneklerden çok daha kısadır.

Charcodes toplamına ne dersiniz:

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

Bu daha zordur, ancak doğru şekilde uygulandığında özyinelemenin 4 bayttan tasarruf ettiğini görebiliriz .map.

Şimdi farklı özyineleme türlerine bakalım:

Ön özyineleme

Bu genellikle en kısa özyineleme türüdür. Giriş iki parçaya bölünmüş olan ave bve fonksiyon bir şey hesaplar ave f(b). Faktöriyel örneğimize geri dönelim:

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

Bu durumda, abir N , bolan , n-1 , ve geri değerdir a*f(b).

Önemli not: Tüm özyinelemeli işlevler , giriş yeterince küçük olduğunda özyinelemeyi durduracak bir yola sahip olmalıdır . Faktöriyel fonksiyonu olarak, bu kontrol edilir n? :1girişi, yani eğer 0 dönüş 1 çağırmadan ftekrar.

Post-özyineleme

Özyineleme sonrası özyineleme öncesiyle benzer, ancak biraz farklıdır. Giriş iki parçaya bölünmüş olan ave bve fonksiyon bir şey hesaplar ave ardından çağırır f(b,a). İkinci argüman genellikle varsayılan bir değere sahiptir (örn.f(a,b=1) ).

Nihai sonuçla özel bir şey yapmanız gerektiğinde ön yineleme iyidir. Örneğin, bir sayı artı 1'in faktörlemesini istiyorsanız:

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

Bununla birlikte, o zaman bile, post- her zaman başka bir işlev içinde özyinelemeyi kullanmaktan daha kısa değildir:

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

Peki ne zaman daha kısa? Bu örnekteki özyinelemenin, özyinelemeden önce değilken, işlev argümanlarının çevresinde parantez gerektirdiğini fark edebilirsiniz. Genel olarak, her iki çözüm de argümanlar etrafında parantez gerektiriyorsa, özyineleme sonrası yaklaşık 2 bayt daha kısadır:

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

(buradaki programlar bu cevaptan alınmıştır) )

En kısa çözümü nasıl bulabilirim?

Genellikle en kısa yöntemi bulmanın tek yolu hepsini denemektir. Bu içerir:

Ve bunlar sadece en yaygın çözümlerdir; En iyi çözüm, bunların bir kombinasyonu veya tamamen farklı bir şey olabilir . En kısa çözümü bulmanın en iyi yolu her şeyi denemek .


1
Değeri için +1 ve zootopia için başka bir +1 eklemek istiyorum
edc65

7

Yapmanın daha kısa yolları .replace


Bir tam alt dizinin tüm örneklerini dize ile bir başkasıyla değiştirmek istiyorsanız, bariz yol şu şekilde olacaktır:

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

Ancak, 1 bayt daha kısa yapabilirsiniz:

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

gBayrak dışında herhangi bir regex özelliğini kullanmak istiyorsanız, bunun artık daha kısa olmadığını unutmayın . Ancak, bir değişkenin tüm örneklerini değiştiriyorsanız, genellikle çok daha kısadır:

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

Bazen bir dizedeki her karakterin üzerinde eşleme yapmak isteyebilirsiniz, her birini bir başkasıyla değiştirmek. Sık sık kendimi bunu yaparken buluyorum:

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

Ancak, .replaceneredeyse her zaman daha kısadır:

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

Şimdi, bir dizgideki her karakterin üzerinde eşleme yapmak istiyorsanız, ancak elde edilen dizgiyi önemsemiyorsanız, .mapgenellikle kurtulabileceğinizden daha iyidir .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

Son durumda, yalnızca bir regex (benzeri /\w/g) ile eşleşen bazı karakterler ilgileniyorsa, o zaman change kullanmak bu demodaki gibi daha iyi olacaktır .
Shieru Asakoto

6

RegEx değişmezlerini ile yazma eval

Regex kurucusu, uzun adı nedeniyle çok hantal olabilir. Bunun yerine, eval ve backticks ile bir değişmez yazın:

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

Değişken ieşittir ise foo, bu aşağıdakileri üretecektir:

/<foo [^>]+/g

Bu eşittir:

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

Ayrıca String.rawart arda ters eğik çizgiden kaçmak zorunda kalmamak için de kullanabilirsiniz .\

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

Bu çıkacaktır:

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

Hangi eşittir:

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

Aklında tut!

String.rawçok fazla bayt alır ve en az dokuz ters eğik çizginiz yoksa , String.rawdaha uzun olacaktır.


İçeri girmene gerek newyok, bu yüzden
Optimizer

5

.forEachvs fordöngüler

Her zaman .mapdöngü için herhangi birini tercih edin. Kolay, anında tasarruf.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • Orijinal için toplam 8 bayt
  • 8 bayt, vs için kaydetti ( % 50 azalma)
  • 22 bayt kaydedilen vs döngü için C tarzı ( % 73 azalma)

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • Orijinal için toplam 16 bayt
  • 2 bayt, vs için kaydedildi ( % 11 azalma)
  • 16 bayt kaydedildi , döngü için C tarzı ( % 50 azalma)

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);
  • Orijinal için toplam 22 bayt
  • 1 bayt , for- in'e göre kaydedildi ( % 4 azalma)
  • 11 bayt kaydedildi , döngü için C tarzı ( % 33 azalma)

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);
  • Orijinal için toplam 19 bayt
  • 2 bayt, vs için kaydedildi ( % 10 azalma)
  • 18 bayt kaydedildi , döngü için C tarzı ( % 49 azalma)

5

Başlatılmamış sayaçları özyinelemede kullanma

Not : Kesinlikle konuşursak, bu ES6'ya özgü değildir. Bununla birlikte, ok işlevlerinin özlü niteliği nedeniyle ES6'da özyinelemeyi kullanmak ve kötüye kullanmak daha mantıklıdır.


kBaşlangıçta sıfıra ayarlanmış ve her bir yinelemede artan bir sayaç kullanan yinelemeli bir işlevle karşılaşmak oldukça yaygındır :

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

Belirli şartlar altında, böyle bir sayacın ilklendirmesini atlamak ve k+1bununla değiştirmek mümkündür -~k:

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

Bu numara genellikle 2 byte kazandırır .

Neden ve ne zaman çalışıyor?

Bunu mümkün kılan formül ~undefined === -1. Böylece, ilk tekrarlamada -~kdeğerlendirilecektir 1. Bir sonraki tekrarlamalar günü, -~kesasen eşdeğerdir -(-k-1)eşit olduğu k+1... 2 en azından aralık [0 yılında tamsayılar için, 31 -1].

Bununla birlikte k = undefined, ilk tekrarlamaya sahip olmanın fonksiyonun davranışını bozmayacağından emin olmalısınız . Özellikle, dahil olan çoğu aritmetik işlemin undefinedsonuçlanacağını aklınızda tutmalısınız NaN.

Örnek 1

Olumlu bir tamsayı verildiğinde n, bu işlev kbölünmeyen en küçük tamsayıyı arar n:

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

Kısaltılabilir:

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

Bunun nedeni çalışır n % undefinedolduğu NaNfalsy olan. İlk yinelemede beklenen sonuç budur.

[Orijinal cevaba bağlantı]

Örnek 2

Pozitif tam sayı göz önüne alındığında n, bu fonksiyon bir tamsayı arar pöyle ki (3**p) - 1 == n:

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

Kısaltılabilir:

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

Bu işe yarıyor çünkü pilk tekrarlamada kullanılmıyor ( n<kyanlış).

[Orijinal cevaba bağlantı]


5

ES6 işlevleri

Matematik

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

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

3 karakter kaydedildi

Math.hypot(...args)Arg karelerinin toplamının kareköküne ihtiyacınız olduğunda kullanışlıdır. Bunu yapmak için ES5 kodu yapmak, yerleşik kullanmaktan çok daha zordur.

İşlev kısa Math.trunc(x)olduğu gibi yardımcı olmaz x|0. (Teşekkürler Mwr247!)

ES5'te yapılacak çok fazla kod alan ancak ES6'da daha kolay olan birçok özellik vardır:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Trigonometrik fonksiyonların hiperbolik eşdeğerini hesaplar.
  • Math.clz32. ES5'te yapmak mümkün olabilir, ancak şimdi daha kolaydır. Bir sayının 32 bitlik temsilindeki baştaki sıfırları sayar.

Orada çok daha fazla, bu yüzden sadece bazı listelemek için gidiyorum:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)dört kat daha uzun x|0.
Mwr247

@ mwr247: Tamam, güncellenecek.
ev3 komutanı

İşte bu fonksiyonlardan birkaçı için bildiğim en kısa ES5 eşdeğerleri: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 bayt daha uzun; daha fazla argümanla daha uzun olur), Math.sign(a) => (a>0)-(a<0)(1 bayt daha kısa, ancak bazı durumlarda NaN
etrafa

@ETHproductions Hipot için argüman dizisine ihtiyacınız var. Math.sign için geçici çözümün -0 için çalıştığından emin misiniz? (-0
Dönmeli

1
@ ev3commander Bunlar sadece kendi ES6 eşdeğerleri için çevrimiçi değiştirmeler anlamına gelir, bu nedenle kullanımların% 99'u için küçültülürler. Bu işlevleri gerçekten yeniden oluşturmak için çok daha fazla kod gerekir. Ayrıca, -0 için özel bir davaya ihtiyaç duyma gerekliliğini görmüyorum, çünkü (AFAIK) manuel olarak belirtmek dışında -0 elde etmenin bir yolu yok ve pratik olarak kod-golf içinde kullanılamıyor. Fakat bunları işaret ettiğiniz için teşekkürler.
ETHProductions,

5

İçin küçük sabit aralıkları optimize etme map()

bağlam

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

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

ikisinden biriyle değiştirilebilir:

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

veya daha sık:

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

Array(N)N

aralığı için optimizasyonlar[0 ..N-1]

ben

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 : Geri arama kodunun uzunluğuF(i) sayılmaz.

aralığı için optimizasyon[1..9]

[1..9]

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

Sayaçsız optimizasyonlar

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

Not : Geri arama kodunun uzunluğuF() sayılmaz.


Gerekmiyor 2**26olmak 2**29?
Shaggy

@Shaggy Heck. İyi yakalama!
Arnauld,

Kendi kendime düzenlemek istemedim çünkü kod körlüğüm var! : D
Shaggy

Kullanarak .keys(), bir lambdaya ihtiyacınız yok:[...Array(10).keys()].map(do_something_with)
uzun lazuli

@ long-lazuli Eğer bir lambdaya ihtiyacınız yoksa ve sadece bir aralık istiyorsanız, muhtemelen haritaya da ihtiyacınız olmaz ...
Arnauld

4

Yıkım işleri

ES6 ödevleri yok etmek için yeni bir sözdizimi sunar; yani, bir değeri parçalara ayırıp her bir parçayı farklı bir değişkene atamak. İşte birkaç örnek:

Dizeler ve diziler

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

Nesneler

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

Bu atamalar fonksiyon parametrelerinde de kullanılabilir:

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

Kaçınmanın başka bir yolu return

Ok fonksiyonları için eval ile kullanmanız gerekir . Sıra dışı bir durumda, bir iç alt işlev kullanarak daha fazla tasarruf edebilirsiniz.

Sıradışı diyorum çünkü

  1. Döndürülen sonuç, döngüde değerlendirilen son ifade olmamalıdır

  2. Döngüden önce (en az) 2 farklı ilklendirme yapılmalıdır.

Bu durumda, parametre olarak geçirilen ilk değerlerden birine sahip olan, geri dönüşsüz bir iç alt işlev kullanabilirsiniz.

Örnek a'dan b'ye kadar bir aralıktaki değerler için exp işlevinin toplamının karşılığını bulun.

Uzun yol - 55 bayt

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

Eval ile - 54 bayt

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

İç işlevli - 53 bayt

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

Daha düşük bir aralık sınırı gerekmeden, ai ve r'nin ilklendirmelerini birleştirebileceğimi ve eval sürümünün daha kısa olduğunu unutmayın.


Örnekte saklamana gerek yoka
14m2

@ l4m2 Anlamadım, lütfen yardım edin ...
edc65

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

@ l4m2 doğru, return a/rdaha iyi bir örnek olurdu
edc65

1
eval hala daha iyi (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")ve bu durumda(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon

4

İkili ve özyinelemeli işlevler için körleme sözdizimini kullanma

İkili fonksiyonlar

Bir işlev varsayılan değeri olmayan tam olarak iki argüman aldığında, körleme sözdizimini kullanmak bir bayttan tasarruf sağlar.

Önce

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

Denilen f(a,b)

Sonra

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

Denilen f(a)(b)

Not : Bu yazı Meta’da bu sözdiziminin geçerliliğini onaylar.

Özyinelemeli fonksiyonlar

Kıvrımlı sözdiziminin kullanılması, özyinelemeli bir işlev birkaç argüman aldığında bazı baytları da kaydedebilir, ancak yalnızca her yineleme arasında bazılarını güncelleştirmesi gerekir.

Örnek

Aşağıdaki işlev, aralıktaki tüm tam sayıların toplamını hesaplar [a,b]:

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

Çünkü atüm süreç boyunca değişmeden kalır, biz kullanarak 3 bayt kaydedebilirsiniz:

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

Not : Yorumlarda Neil tarafından fark edildiği gibi, bir tartışmanın özyinelemeli fonksiyona açıkça geçilmemesi gerçeğinin değişmez olarak kabul edilmesi gerektiği anlamına gelmez. Gerekirse, biz değiştirebilir afonksiyonu ile kodundaki a++, a--veya benzeri her türlü sözdizimi.


Son örnek, her özyinelemeli çağrı için a=>F=b=>a>b?0:a+++F(b)değiştirilerek olarak yazılabilir a. Bu, bu durumda yardımcı olmuyor ancak daha fazla argüman olan durumlarda baytları kurtarabilir.
Neil

Heh, sadece bunun için bir ipucu yazmayı düşünüyordum :-)
ETHproductions

4

Asallık testi işlevi

Aşağıdaki 28 bayt işlevi true, asal sayılar ve astar falseolmayanlar için döndürür :

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

Bu, başka şeyleri hesaplamak için kolayca değiştirilebilir. Örneğin, bu 39 bayt işlevi, bir sayıya eşit veya daha küçük olan asal sayıları sayar:

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

Zaten nilkelliği kontrol etmek istediğiniz bir değişkeniniz varsa, ilkellik işlevi biraz basitleştirilebilir:

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

Nasıl çalışır

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.

Not: Bu, 12345 gibi yeterince büyük bir girişle çağrıldığında "çok fazla özyineleme" hatasıyla başarısız olur. Bunu bir döngü ile çözebilirsiniz:

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

1
Ancak 12345 kadar küçük bir giriş için çok fazla özyinelemeyle başarısız olun
edc65

x==1Muhtemelen x<2tasarruf için olabilir .
Hesap MakinesiFeline

@CalculatorFeline Teşekkürler, ama sonra başarısız olur 1ya da 0(çünkü ya da sırasıyla xolurdu )0-1
ETHproductions

Bazı durumlarda faydalı olabilir. Ayrıca, !~-x-0 bayt için.
Hesap MakinesiFeline

3

Array#concat() ve yayılma operatörü

Bu büyük ölçüde duruma bağlıdır.


Çoklu dizilerin birleştirilmesi.

Klonlamadıkça concat işlevini tercih edin.

0 bayt kaydedildi

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

Boşa harcanan 3 bayt

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

3 bayt kaydedildi

a.concat()
[...a]

6 bayt kaydedildi

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

Önceden var olan bir diziyi kullanmayı tercih etmek Array#concat() .

Kolay 4 bayt kaydedildi

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

3

Ara sonucu döndür

Virgül operatörünü kullanarak son değeri döndüren bir dizi ifadeyi çalıştırabileceğinizi biliyorsunuz. Ancak değişmez dizi sözdizimini kötüye kullanmak, herhangi bir ara değere dönebilirsiniz. Örneğin .map () işlevinde kullanışlıdır.

// 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
Unutma, elbette, bu .join('')olabilir.join``
Cyoce

3

İşlev parametresi varsayılanlarını ayarlama

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

Bu gerçekten çok kullanışlı ...

Ancak, _=>_||'asdf'fonksiyona yalnızca bir (faydalı) argüman ilettiğinizde, bunun gibi bir şeyin daha kısa olduğunu anladığınızdan emin olun .


1
Bir OR kullanmanın _=>_||'asdf'çoğu durumda genellikle daha kısa olduğunu not ediyorum
Downgoat

@Downgoat Bunun (boş dize) "asdf"bir giriş için geri döndüğünü unutmayın "".
ETHProductions

2
undefinedAçıkça bu değeri geçseniz bile , argüman ne zaman olursa olsun varsayılanın değerlendirildiğini unutmayın . Örneğin, [...Array(n)].map((a,b,c)=>b)her zaman geçerse undefinediçin ave bu nedenle, (değil açısından her ne kadar bunun için varsayılan bir değer sağlayabilir b).
Neil,

3

evalOk işlevleri için ayraç yerine kullanın

Ok fonksiyonları harika. Onlar şeklinde x=>y, xbir argümandır ve ydönüş değeridir. Bununla birlikte, örneğin bir kontrol yapısı kullanmanız gerekiyorsa while, örneğin parantez koymak zorunda kalacaksınız =>{while(){};return}. Ancak, bunun üstesinden gelebiliriz; Neyse ki, evalişlev bir dize alır, bu dizeyi JS kodu olarak değerlendirir ve en son değerlendirilen ifadeyi döndürür . Örneğin, bu ikisini karşılaştırın:

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

Kodumuzu daha da kısaltmak için bu kavramın bir uzantısını kullanabiliriz: evalkontrol yapıları gözünde en son değerlendirilen ifadelerini de verir. Örneğin:

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

3

ES6'da Golf Mantıksal İşlemleri

"GLOE (S6)"

Genel Mantık

Ve ifadeleri kurduğunuzu sve t. Aşağıdaki değişikliklerden herhangi birini kullanıp kullanamayacağınıza bakın:

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

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

(; İe Bunlar sırası yanlışsa çalışmayabilir +ve *daha düşük mertebeden önceliğe sahiptir ||ve &&yapın.)

Ayrıca, işte bazı kullanışlı mantıksal ifadeler:

  • Ya doğru sya tda doğru / XOR:s^t
  • sve taynı gerçek değer: !s^tyas==t

Dizi mantığı

Tüm akoşul yerine getirme üyeleri 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)

En az bir üye aşartı yerine getirir 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)

Hiç bir üyesi atatmin durumu p: !a.some(p).

Öğe edizide var 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

Öğe dizide emevcut değila :

!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++]);

Genelde sırasıyla ve &&ve ||olarak kullanırım . Fakat bunun daha mantıklı tabanlı programlarda nasıl faydalı olacağını görebiliyorum. Bununla ilgili bir problem , örneğin ve her ikisinin de truthy olduğudur, ama değildir. x?y:xx?x:y+3-33+-3
ETHProductions

@ETHproductions Ah, haklısın; bu bir kenar davası. -eğer işe yarayabilirdi s != t.
Conor O'Brien,

a.filter(t=>t==e).length==a.lengthyanlış. Olması gereken!a.filter(t=>t==e).length
ETHproductions 23:16

@ETHproductions doğru sen!
Conor O'Brien,

3

Tekrarlanan işlev çağrılarını kısaltın

Tuval işleme gibi uzun ish ismine sahip bir işleve yapılan çağrıları tekrarladıysanız:

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

Kısaltmanın geleneksel yolu, işlev ismini diğer isimlendirmek olacaktır:

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

Yeterli görüşmeniz varsa, sizin için işi yapan bir işlev oluşturmak için daha iyi bir yoldur:

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

İşlev çağrılarının çoğu zincirlenmişse, işlevin kendisini döndürmesini sağlayarak, art arda gelen her çağrının iki baytını kesmenize olanak sağlar:

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

Örnek kullanım: 1 , 2


1
bağlama işleci ile kısaltabilirsiniz :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@Downgoat Teşekkürler, hangi tarayıcılar bunu destekliyor? (Ayrıca, ikinci aramada hata çıkacağını gördüklerimden beri c.lineTo, doğal olarak kendisini geri
getirmiyor

ES7 özelliği olduğundan babel ile ovalamanız gerekir
Downgoat

3

Bağlayıcı işleci ::

Ciltleme operatörü, tekrarlanan işlevler üzerinden baytların kısaltılmasına yardımcı olmak için kullanılabilir:

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

Ayrıca, işlevi farklı bir thisörnekte kullanmak istiyorsanız :

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

Çok fazla veri depolarken virgüllerden kaçınmak

Bir dizide saklamanız gereken çok fazla veriye (yani, indeksler, karakterler,…) sahipseniz, tüm virgülleri uzak bırakmaktan daha iyi olabilirsiniz. Bu, her veri parçasının aynı dize uzunluğuna sahip olması durumunda en iyi şekilde çalışır, 1 açıkça optimumdur.

43 Bayt (taban çizgisi)

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

34 Bayt (virgül yok)

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

Eğer hazırsanız sizin dizi erişimi değiştirmek , bunu gibi aynı değerleri depolamak, daha da bu düşürebilir:

27 Bayt (aynı veriler, yalnızca dizi erişimini değiştirir)

a=[..."376189452543276543"]

Neden sadece son blok vurgulanıyor?
CalculatorFeline

@CalculatorFeline Teşekkürler, düzeltildi.
Chiru
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.