Поради щодо гольфу в ECMAScript 6 і вище


88

Це схоже на інші "Поради щодо гри в гольф в <...>", але конкретно орієнтовано на новіші функції JavaScript, представлені в ECMAScript 6 і вище.

JavaScript по своїй суті є дуже багатослівним мову, function(){}, .forEach(), перетворення рядка в масив, масив типу об'єкта в масив, і т.д., і т.д., супер роздувається і не здорові для гри в гольф.

ES6 +, з іншого боку, має надзвичайно зручні функції та зменшений набір. x=>y, [...x]тощо, лише деякі приклади.

Будь ласка, опублікуйте кілька приємних хитрощів, які можуть допомогти відрізати ці кілька зайвих байтів від вашого коду.

ПРИМІТКА. Прийоми для ES5 вже доступні в Порад щодо гольфу в JavaScript ; відповіді на цю тему повинні зосереджуватися на хитрощах, доступних лише у ES6 та інших майбутніх версіях ES.

Однак ця тема також призначена для користувачів, які в даний час гольф використовують функції ES5. Відповіді також можуть містити поради, які допоможуть їм зрозуміти та відзначити функції ES6 у їх стилі кодування ES5.

Відповіді:


42

Оператор розповсюдження ...

Оператор розповсюдження перетворює значення масиву у список, розділений комами.

Використовуйте випадок 1:

Безпосередньо використовуйте масив, де функція очікує список

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

Скористайтеся випадком 2:

Створіть літерал масиву з ітерабельного (зазвичай рядка)

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

Використовуйте випадок 3:

Заявіть змінну кількість аргументів функції

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

Див. Мозіла док


3
Тепер у мене тут є голова. Очевидно, хтось зазначив щось жахливо неправильне в цій підказці, будучи занадто сором'язливим, щоб залишити коментар і пояснити, що ...
edc65

Це виглядає гаразд. Може, це була відсутність крапки з комою? ;) (btw, ви також можете використовувати його як параметри спокою, як splats у Ruby)
gcampbell

Ви можете додати, що він також має випадок використання у функціях підписів :)
Фелікс Домбек

Misclick не мав на увазі анонсації
Stan Strum

@StanStrum це буває. Я проведу невелике оновлення до цієї публікації, щоб ви могли врешті змінити свій голос (або ви вже зробили?)
edc65

21

Трюки тут вивчили з моменту приєднання

Моя основна мова програмування - JS та здебільшого ES6. Оскільки я приєднався до цього сайту тиждень тому, я навчився багато корисних хитрощів від колег. Я поєдную деякі з них тут. Усі кредити для громади.

Функції стрілок і петлі

Усі ми знаємо, що функції стрілок економлять багато байтів

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

Але потрібно пам’ятати про кілька речей

  • Спробуйте об'єднати кілька висловлювань, використовуючи, ,тобто (a=b,a.map(d))- Тут повернене значення є останнім виразомa.map(d)
  • якщо ваша do somethingчастина більше ніж одне твердження, вам потрібно додати навколишні {}дужки.
  • Якщо існують довколишні {}дужки, вам потрібно додати явну заяву повернення.

Вищезазначене справедливо багато разів, коли у вас є петлі. Тож щось на кшталт:

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

Тут я витрачаю щонайменше 9 символів через повернення. Це можна оптимізувати.

  • Намагайтеся уникати петель. Використовуйте .mapабо .everyабо .someзамість цього. Зауважте, що якщо ви хочете змінити той самий масив, який ви переглядаєте, він не вдасться.
  • Оберніть цикл у функції стрілки закриття, перетворивши основну функцію стрілки у єдине твердження.

Отже, вищезазначене стає:

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]

вилучені символи: {}return

додано символів: (){}>|

Зауважте, як я називаю метод закриття, який правильно заповнює змінну, nа потім, оскільки метод закриття нічого не повертає (тобто повертається undefined), я побіжно або це і повертаю масив nвсе в одному операторі функції зовнішньої стрілкиu

Коми та крапки з комою

Уникайте їх, що коли-небудь,

Якщо ви декларуєте змінні в циклі, або подібні до згаданих у попередньому розділі, використовуючи ,відокремлені оператори, щоб мати стрілочні функції однієї конфігурації, то ви можете скористатися декількома хитромудрими хитрощами, щоб уникнути цих ,або ;відхилити останні кілька байтів.

Розглянемо цей код:

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

Тут я закликаю безліч методів ініціалізації багатьох змінних. Кожна ініціалізація використовує ,або ;. Це можна переписати як:

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

Зверніть увагу, як я використовую той факт, що метод не турбує передану йому змінну і використовую цей факт для гоління 3-х байт.

Різне

.search замість .indexOf

Обидва дають однаковий результат, але searchкоротший. Хоча пошук очікує регулярного вираження, тому використовуйте його з розумом.

`Рядки шаблону`

Це дуже зручно, коли вам доведеться складати одну або кілька рядкових частин на основі певних умов.

Візьмемо наступний приклад для виведення лайки в JS

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

vs.

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

У рядку шаблону, який представляє собою рядок всередині двох зворотних лапок (`), все, що знаходиться всередині a ${ }, трактується як код і оцінюється, щоб вставити отриману відповідь у рядок.

Я опублікую ще кілька трюків пізніше. Щасливого гольфу!


1
.search коротше, використовуйте його коли це можливо! але це не те саме .indexOf. .search хоче, а regexpне рядок. Спробуйте'abc'.search('.')
edc65

@ edc65 Оновлено!
Оптимізатор

Ви можете змінити оригінальний масив методами екземпляра. Другий - поточний індекс, а третій - ітераційний масив.
Ісія Медоуз

8
"Приєднався до сайту тиждень тому" - 21.4k представник ...
GamrCorps

2
Крім того .map, рекурсія - це ще одна техніка, яка іноді може допомогти вам перетворити forцикл у вираз.
Ніл

20

Використання скорочень властивостей

Скорочення властивостей дозволяють встановлювати змінні значенням масивів:

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

Це також можна використовувати як:

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

Ви можете навіть використовувати це для зворотного зміни змінних:

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

Ви також можете використовувати це для скорочення slice()функцій.

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

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

Базові перетворення

ES6 забезпечує набагато коротший спосіб перетворення форми Base-2 (двійкові) та Base-8 (восьмерична) в десяткову:

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

+може використовуватися для перетворення двійкової, восьмеричної або шістнадцяткової рядка в десяткове число. Ви можете використовувати 0b, 0oі, і 0xдля двійкових, восьмеричних і шестигранних відповідно:

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

Якщо ви використовуєте це> 7 разів, то використання parseIntта перейменування буде коротше :

(p=parseInt)(v,2)

Тепер pйого можна використовувати parseInt, заощаджуючи багато байтів у довгостроковій перспективі.


Базовий трюк конверсії приємний, але більш імовірно, що число перетворення буде у формі змінної замість буквальної; у такому випадку воно стає набагато довшим.
Оптимізатор

1
'0x'+v-0ще коротше, але може не працювати так добре в деяких сценаріях.
ETHproductions

1
До речі, 0767(ES5) коротше позначення 0o767(ES6).
Каміло Мартін

@CamiloMartin 0767- це нестандартне розширення, і це в явному режимі заборонено.
Оріол

1
@Oriol суворий режим був поганим мемом. Це не допомогло в продуктивності, насправді не змусило вас написати хороший код і ніколи не стане за замовчуванням. 0- попередньо встановлені восьмеричні літерали нікуди не поділяються, і є настільки ж дійсними шпальтами 0o.
Каміло Мартін

19

Використання шаблонів рядків з функціями

Коли в якості аргументів у вас функція з одним рядком. Ви можете пропустити це, ()якщо у вас немає жодних виразів:

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

9
Будьте попереджені, це фактично передає масив. fun`string` те саме, що fun(["string"]), ні fun("string"). Це добре для функцій, які передаються на рядок, як alert, але для інших це може спричинити проблеми. Більше інформації дивіться у статті MDN
Cyoce

5
Короткий довідник: fun`foo${1}bar${2}bazрівнозначний дзвінкуfun(["foo","bar","baz"],1,2)
Cyoce

14

Поняття масиву (Firefox 30-57)

Примітка: розуміння масиву ніколи не були стандартизовані, і вони застаріли за допомогою Firefox 58. Використовуйте на свій страх.


Спочатку специфікація ECMAScript 7 містила купу нових функцій на основі масиву. Хоча більшість з них не перетворити його в доопрацьованій версії, підтримка Firefox (редактор) , можливо , найбільший з цих функцій: фантазії новий синтаксис , який може замінити .filterі .mapз for(a of b)синтаксисом. Ось приклад:

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

Як бачите, два рядки не всі такі різні, крім другого, що не містять об'ємних ключових слів і функцій стрілок. Але це пояснює лише замовлення .filter().map(); що станеться, якщо у вас .map().filter()замість цього? Це дійсно залежить від ситуації:

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]

Або що , якщо ви хочете або .map або .filter ? Ну, зазвичай виходить менш гаразд:

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]

Так що моя порада використовувати осягнення масиву , де ви зазвичай використовуєте .map і .filter , але не тільки один чи інші.

Строкові розуміння

Приємно в розумінні ES7 - це те, що на відміну від функцій, характерних для масиву, таких як .mapі .filter, вони можуть використовуватися на будь-якому ітерабельному об'єкті, а не лише на масивах. Це особливо корисно при роботі зі струнами. Наприклад, якщо ви хочете запустити кожен символ cу рядку через c.charCodeAt():

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

Це два байти, збережені в досить малому масштабі. А що робити, якщо ви хочете відфільтрувати певні символи в рядку? Наприклад, цей зберігає лише великі літери:

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

Хм, це не коротше. Але якщо поєднати два:

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

Нічого собі, цілих 10 байт збережено!

Ще однією перевагою розуміння рядків є те, що рядки з жорстким кодом економлять зайвий байт, оскільки ви можете опустити простір після 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]

Індексація

Зрозуміння масиву ускладнює отримання поточного індексу в рядку / масиві, але це можна зробити:

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

Головне, на що слід бути обережним, - це переконатися, що індекс збільшується щоразу , а не лише коли виконується умова.

Розуміння генератора

Розуміння генератора мають в основному такий же синтаксис, як і розуміння масиву; просто замініть дужки дужками:

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

Це створює генератор, який функціонує приблизно так само, як масив, але це історія для іншої відповіді.

Підсумок

В основному, хоча розуміння зазвичай коротше .map().filter(), все зводиться до специфіки ситуації. Найкраще спробувати обидва способи і побачити, що виходить краще.

PS Не соромтеся запропонувати ще одну пораду щодо розуміння чи спосіб покращити цю відповідь!


Ось хитрість для діапазонів, які врятують ще пару символів:(x,y)=>[...Array(y-x)].map(a=>x++)
Mwr247

2
Ви можете відрізати ще 11 байт, щоб зробити діапазон від 0 до х:x=>[...Array(x).keys()]
Mwr247

Останній для розуміння там: n=>[for(x of Array(n).keys())if(/1/.test(x))x](економить 7 байтів)
Mwr247

@ Mwr247 Насправді я зараз бачу, що діапазони, як правило, не такі короткі з розумінням, як з іншими приємними особливостями ES6. Я замість цього додаю в розділ про рядки і дозволю вам обробляти діапазони.
ETHproductions

Варто зазначити, що Поняття масиву застаріли та видалені з усіх останніх версій JavaScript. Див. Документи MDN з цього питання.
Кіфер Рурк

13

Функції вирази в ES6 використовують позначення стрілки, і це допомагає значно економити байти в порівнянні з версією ES5:

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

Якщо у вашої функції є лише один параметр, ви можете опустити дужки, щоб зберегти два байти:

f=x=>x+1

Якщо у вашої функції взагалі немає параметрів, оголосьте її так, ніби вона мала один байт:

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

Остерігайтеся: функції стрілок точно не такі, як function () {}. Правила для thisних різні (і краще ІМО). Див. Документи


2
Але коли ви гольф-ING, ви взагалі не хвилює і thisт.д.
оптимізатор

1
Як правило, ні, але це застереження, про яке ви ніколи не дізнаєтесь, коли з’являться. Ламбдам також частіше не потрібно функціонувати місцевим зв'язком у виробництві.
Isiah Meadows

Крім того, якщо ви хочете взяти всі свої аргументи, ви можете скористатися функцією аргументу "відпочинок", наприклад, f=(...x)=>x матиме це f(1,2,3) => [1,2,3].
Conor O'Brien

1
Ось підказка, характерна для цього сайту: якщо ви відповідаєте за допомогою функції, яка приймає форму, (x,y)=>...ви можете зберегти байт із заправкою , замінивши його наx=>y=>...
Cyoce

12

Використання evalдля стрілочних функцій з декількома операторами та areturn

Один із більш смішних витівків, на які я натрапив ...

Уявіть просту функцію стрілки, яка потребує декількох висловлювань та a return.

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

Проста функція, що приймає єдиний параметр a, який повторює всі цілі числа [0, a), і додає їх до кінця вихідного рядка o, який повертається. Наприклад, виклик цього 4параметра як параметра дасть результат 0123.

Зауважте, що цю стрілочну функцію потрібно було загорнути в дужки {}і мати return oкінець.

Ця перша спроба важить 39 байт .

Непогано, але, використовуючи eval, ми можемо покращити це.

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

Ця функція видалила дужки та оператор return, загорнувши код у evalта просто зробивши останню заяву в evalоцінці o. Це призводить evalдо повернення o, що, в свою чергу, призводить до повернення функції o, оскільки тепер це єдине твердження.

Ця вдосконалена спроба важить в 38 байт , заощаджуючи один байт від оригіналу.

Але зачекай, є ще більше! Виписки Eval повертають незалежно від їх останньої заяви. У цьому випадку o+=iоцінюється до o, тому нам це не потрібно ;o! (Спасибі, edc65!)

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

Ця остання спроба важить лише 36 байт - це 3 байт заощадження за оригінал!


Цей прийом можна поширити на будь-який загальний випадок, коли функції стрілки потрібно повернути значення та мати кілька операторів (які не можна комбінувати іншими способами)

b=>{statement1;statement2;return v}

стає

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

збереження байта.

Якщо statement2оцінювати v, це може бути

b=>eval('statement1;statement2')

економія в цілому 3 байти.


1
Я думаю, що просто написати анонімну функцію може бути ще коротше
Downgoat

@vihan Так, обидві ці функції можна зробити анонімними, щоб зберегти по 2 байти. Економія на один байт все ще залишається.
jrich

1
Але ще краще: eval повертає останнє оцінене вираження, тож вам не потрібно ;o- спробуйте:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
Але рядки шаблону!
Conor O'Brien

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ Потрібно пояснити, як тут будуть працювати рядки шаблонів, використовуючи функцію прикладу як контекст?
WallyWest

10

Віддайте перевагу рядкам шаблону нових рядків над "\ n"

Це почне окупатися навіть одним новим символом рядка у вашому коді. Одним із випадків використання може бути:

(16 байт)

array.join("\n")

(15 байт)

array.join(`
`)

Оновлення: Ви навіть можете залишити дужки через теги рядків шаблону (спасибі, edc65!):

(13 байт)

array.join`
`

5
Але ще краще, ви можете уникнути дужок. Прочитайте документи ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ), щоб дізнатися, чому
edc65

Ага, правильно. Дякую, я додав.
Чіру

9

Заповнення масивів - статичні значення та динамічні діапазони

Спочатку я залишав це як коментарі під розумінням, але оскільки цей пост був зосереджений насамперед на розуміннях, я зрозумів, що було б добре віддати цьому своє місце.

ES6 дав нам можливість заповнювати масиви статичними значеннями без використання циклів:

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

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

Обидва повертають масив довжиною x, заповнений значенням 0.

Якщо ви хочете заповнити масиви динамічними значеннями (такими як діапазон від 0 ... x), результат трохи довший (хоча все ж коротший за старий):

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

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

Обидва повертають масив довжиною x, починаючи зі значення 0 і закінчуючи x-1.

Причина того, що вам потрібно .fill()в тому, полягає в тому, що просто ініціалізація масиву не дозволить вам відобразити його. Тобто, це x=>Array(x).map((a,i)=>i)поверне порожній масив. Ви також можете обійти необхідність заповнення (і тим самим зробити її ще коротшою), скориставшись оператором розповсюдження так:

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

Використовуючи оператор і .keys()функцію розповсюдження , тепер ви можете зробити короткий діапазон 0 ... x:

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

Якщо ви хочете користувальницький діапазон від x ... y або загальний спеціалізований діапазон (наприклад, парні цифри), ви можете позбутися .keys()і просто скористатися .map()або скористатися .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)

Ось пропозиція для другого прикладу: x=>Array(x).fill(i=0).map(a=>i++)Також я не впевнений, що 0 в .fill(0)потрібен. Ви пробували без нього?
ETHproductions

@ETHproductions Ви маєте рацію, я забув, що 0 не потрібен для заповнення перед картою. Це робить його на 1 символ коротшим від запропонованого, тому я буду тримати його таким. Дякую!
Mwr247

Крім того, для останнього прикладу a=>a%2-1добре працює, як і це a=>a%2<1.
ETHproductions

1
Новий трюк, який я навчився: [...Array(x)]працює так само добре Array(x).fill(), і на 2 байти коротше. x=>[...Array(x)].map((a,i)=>i)
ETHproductions

1
@yonatanmn Дуже приємно! Лише коментарі будуть 1) 1/4приклад буде коротше виписаний [0,0,0,0], і 2) строговані функції мають специфіку реалізації, тому не повертатимуть надійну довжину ( Map32 байти в Chrome, але 36 байтів у Firefox).
Mwr247

9

Повернення значень у функціях стрілки

Загальновідомо, що якщо одне твердження слідує за оголошенням функції стрілки, воно повертає результат цього оператора:

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

-7 байт

Тож коли це можливо, комбінуйте кілька заяв в одне. Це найпростіше зробити, обклавши твердження в дужки і розділивши їх комами:

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

-8 байт

Але якщо є лише два твердження, їх зазвичай можливо (і коротше) поєднувати з &&або ||:

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 байт

Нарешті, якщо ви використовуєте карту (або подібну) і вам потрібно повернути номер, і ви можете гарантувати, що карта ніколи не поверне масив довжиною 1 довжини з номером, ви можете повернути номер за допомогою |:

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

У цьому останньому прикладі ви також повинні бути впевнені, що число завжди буде цілим числом.
ETHproductions

8

Випадкові хакерські шаблони

Ця функція складається з двох рядків (тобто перетворюється "abc","de"на "adbec"):

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

Зауважте, що це працює лише тоді, коли xдовше ніж y. Як це працює, запитаєте ви? String.rawрозроблений як тег шаблону, наприклад:

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

Це в основному дзвінки String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), хоча це не так просто. Масив шаблонів також має особливу rawвластивість, яка в основному є копією масиву, але з необробленими рядками. String.raw(x, ...args)в основному повертається x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...і так далі, поки не xзакінчиться елементів.

Тож тепер, коли ми знаємо, як String.rawпрацює, ми можемо використовувати це на нашу користь:

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"

Звичайно, для останнього f=(x,y)=>x.split``.join(y)це набагато коротше, але ви розумієте .

Ось пара функцій, які також працюють, якщо xі 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 .


7

Як грати в гольф з рекурсією

Рекурсія, хоча і не найшвидший варіант, дуже часто є найкоротшою. Як правило, рекурсія є найкоротшою, якщо рішення може бути спрощене до рішення меншої частини завдання, особливо якщо вхід є числом або рядком. Наприклад, якщо f("abcd")це можна обчислити з, "a"і f("bcd"), як правило, найкраще використовувати рекурсію.

Візьмемо, наприклад, факториал:

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

У цьому прикладі рекурсія, очевидно, значно коротша, ніж будь-який інший варіант.

Як щодо суми графічних кодів:

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

Це складніше, але ми можемо бачити, що при правильному виконанні рекурсія економить 4 байти .map.

Тепер розглянемо різні типи рекурсії:

Попередня рекурсія

Зазвичай це найкоротший тип рекурсії. Вхід розділений на дві частини aі b, і функція обчислює щось з aі f(b). Повернувшись до нашого факторіального прикладу:

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

У цьому випадку aє n , bє n-1 , і повернене значення є a*f(b).

Важлива примітка: Усі рекурсивні функції повинні мати спосіб зупинити повторне повторення, коли вхід достатньо малий. У функціональній функції це контролюється за допомогою n? :1, тобто, якщо вхід 0 , поверніть 1, не викликаючи fзнову.

Пострекурсія

Пострекурсія схожа на дорекурсію, але дещо відрізняється. Вхід розбивається на дві частини aі b, а функція обчислює щось з a, потім викликає f(b,a). Другий аргумент зазвичай має значення за замовчуванням (тобто f(a,b=1)).

Попередня рекурсія хороша, коли вам потрібно зробити щось особливе з кінцевим результатом. Наприклад, якщо ви хочете факторіал числа плюс 1:

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

Однак навіть після цього пост - не завжди коротше, ніж використання попередньої рекурсії в межах іншої функції:

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

То коли це коротше? Ви можете помітити, що після рекурсії в цьому прикладі потрібні круглі дужки навколо аргументів функції, тоді як попередня рекурсія цього не робила. Як правило, якщо обом рішенням потрібні дужки навколо аргументів, після рекурсії на 2 байти коротше:

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

(програми, взяті з цієї відповіді )

Як знайти найкоротше рішення

Зазвичай єдиний спосіб знайти найкоротший метод - спробувати їх усі. Це включає:

І це лише найпоширеніші рішення; найкращим рішенням може бути поєднання цих, а то і зовсім зовсім інших . Найкращий спосіб знайти найкоротший спосіб - спробувати все .


1
+1 за його значення, і я хотів би додати ще +1 для zootopia
edc65

7

Коротші способи зробити .replace


Якщо ви хочете замінити всі екземпляри однієї точної підрядки на іншу в рядку, очевидним способом буде:

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

Однак ви можете зробити на 1 байт коротше:

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

Зауважте, що це більше не коротше, якщо ви хочете використовувати будь-які функції регулярного вираження, крім gпрапора. Однак якщо ви замінюєте всі екземпляри змінної, зазвичай це набагато коротше:

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

Іноді вам захочеться відображати кожну таблицю в рядку, замінюючи кожну іншою. Мені часто доводиться робити це:

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

Однак .replaceмайже завжди коротше:

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

Тепер, якщо ви хочете відображати кожну таблицю в рядку, але не піклуватися про отриманий рядок, .mapзазвичай краще, тому що ви можете позбутися .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

В останньому випадку, якщо /\w/gзацікавлені лише певні символи, що відповідають рівню (наприклад ), то використовувати заміну буде набагато краще, як у цій демонстрації .
Шиєру Асакото

6

Написання літералів RegEx за допомогою eval

Конструктор регулярних виразів може бути дуже об'ємним через його довгу назву. Замість цього напишіть літерал з eval та backticks:

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

Якщо змінна iдорівнює foo, це призведе до генерування:

/<foo [^>]+/g

Це дорівнює:

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

Ви також можете використовувати, String.rawщоб уникнути повторного відхилення від нахилів\

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

Це виведе:

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

Що дорівнює:

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

Мати на увазі!

String.rawзаймає багато байт , і якщо у вас є по крайней мере дев'ять зворотних косих рис, String.rawбуде більше.


Вам не потрібно newтам, тому використання конструктора фактично коротше для другого прикладу
Optimizer

5

.forEachпроти forпетель

Завжди віддайте перевагу .mapбудь-якому для циклу. Легка, миттєва економія.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • 8 байт для оригіналу
  • 8 байтів, збережених проти (за 50% зменшення)
  • 22 байти збережено проти стилю C для циклу ( зменшення на 73% )

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • 16 байт для оригіналу
  • 2 байти, збережені проти за (з 11% зменшення)
  • 16 байт збережено проти стилю C для циклу ( 50% зменшення)

a.map((x,i)=>f(x,i,0))
for(i in a)f(a[i],i,0);
for(i=0;i<a.length;)f(a[i],i++,0);
  • 22 байти для оригіналу
  • 1 байт збережено порівняно з додатком ( зменшення на 4% )
  • 11 байт збережено проти стилю C для циклу ( зменшення на 33% )

a.map(x=>f(x)&g(x))
for(x of a)f(x),g(x);
for(i=0;i<a.length;)f(x=a[i++]),g(x);
  • 19 байт для оригіналу
  • 2 байти, збережені проти за ( 10% зменшення)
  • 18 байт збережено проти стилю C для циклу ( зменшення на 49% )

5

Використання неініціалізованих лічильників у рекурсії

Примітка : строго кажучи, це не стосується ES6. Однак є сенс використовувати та зловживати рекурсією в ES6 через короткий характер функцій стрілок.


Досить часто зустрічається рекурсивна функція, яка використовує лічильник, kспочатку встановлений на нуль і посилений при кожній ітерації:

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

За певних обставин можна ініціалізувати такий лічильник і замінити k+1на -~k:

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

Цей трюк зазвичай економить 2 байти .

Чому і коли це працює?

Формула, яка це робить можливою, є ~undefined === -1. Отже, на першій ітерації -~kбуде оцінено до 1. На наступних ітераціях, -~kпо суті, еквівалентний -(-k-1)рівним k+1, принаймні, для цілих чисел у діапазоні [0… 2 31 -1].

Однак ви повинні переконатися, що наявність k = undefinedпершої ітерації не порушить поведінку функції. Особливо слід пам’ятати, що більшість арифметичних операцій, що стосуються undefinedцього, призведуть до NaN.

Приклад №1

Враховуючи додатне ціле число n, ця функція шукає найменше ціле число k, яке не ділиться n:

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

Його можна скоротити до:

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

Це працює , тому що n % undefinedце NaN, що falsy. Це очікуваний результат на першій ітерації.

[Посилання на оригінальну відповідь]

Приклад №2

Враховуючи додатне ціле число n, ця функція шукає ціле число pтаке (3**p) - 1 == n:

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

Його можна скоротити до:

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

Це працює, оскільки pвзагалі не використовується під час першої ітерації ( n<kбудучи помилковою).

[Посилання на оригінальну відповідь]


5

ES6 функції

Математика

Math.cbrt(x)зберігає символи, ніж Math.pow(x,1/3).

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

3 символи збережено

Math.hypot(...args)корисно, коли вам потрібен квадратний корінь суми квадратів арг. Зробити код ES5 зробити це набагато складніше, ніж використовувати вбудований.

Ця функція Math.trunc(x)не буде корисною, оскільки x|0вона коротша. (Спасибі Mwr247!)

У ES5 існує багато властивостей, які вимагають багато коду, але в ES6 простіше:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Обчислює гіперболічний еквівалент тригонометричних функцій.
  • Math.clz32. Можливо, це можна зробити в ES5, але зараз простіше. Підраховує провідні нулі в 32-бітовому поданні числа.

Є набагато більше, так що я просто буду перераховувати деякі з них :
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)в чотири рази довше, ніж x|0.
Mwr247

@ mwr247: Гаразд, оновиться.
ev3commander

Ось найкоротші еквіваленти ES5, про які я знаю, для пари цих функцій: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(на 3 байти довше; стає ще довше з більшою кількістю аргументів), Math.sign(a) => (a>0)-(a<0)(на 1 байт коротше, але в деяких випадках потрібні оточуючі дужки; вони не можуть працювати з NaN)
ETHproductions

@ETHproductions Вам потрібен масив аргументів для гіпота (рішення es5). А ви впевнені, що рішення для Math.sign працює на -0? (Слід повернути -0)
ev3commander

1
@ ev3commander Вони призначені лише як внутрішні заміни відповідних еквівалентів ES6, тому їх зменшують на 99% використання. Для справжнього відтворення цих функцій знадобиться набагато більше коду. Крім того, я не бачу причин вимагати наявності спеціального випадку для -0, оскільки (AFAIK) немає жодного способу отримати -0, окрім вручну вказуючи його, і практично не використовувати його в коді-гольф. Але дякую, що вказали на ці речі.
ETHproductions

5

Оптимізація малих постійних діапазонів для map()

Контекст

map()for[0..N1]

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

можна замінити будь-яким:

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

або частіше:

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

Array(N)N - мала константа.

[0..N1] , з лічильником

i

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 6       | use a raw array of integers          | [0,1,2,3].map(i=>F(i))          | 2N+10
N = 7       | use either a raw array of integers   | [0,1,2,3,4,5,6].map(i=>F(i))    | 24
            | or a string if your code can operate | [...'0123456'].map(i=>F(i))     | 23
            | with characters rather than integers |                                 |
8 ≤ N ≤ 9   | use scientific notation 1e[N-1]      | [...1e7+''].map((_,i)=>F(i))    | 24
N = 10      | use scientific notation 1e9          | [...1e9+''].map((_,i)=>F(i))    | 24
            | or the ES7 expression 2**29+'4' if   | [...2**29+'4'].map(i=>F(i))     | 23
            | the order doesn't matter and your    |                                 |
            | code can operate with characters     |  (order: 5,3,6,8,7,0,9,1,2,4)   |
            | rather than integers                 |                                 |
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map((_,i)=>F(i))   | 25
N = 18      | use the fraction 1/3                 | [...1/3+''].map((_,i)=>F(i))    | 24
N = 19      | use the fraction 1/6                 | [...1/6+''].map((_,i)=>F(i))    | 24
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map((_,i)=>F(i))   | 25
N = 22      | use scientific notation -1e20        | [...-1e20+''].map((_,i)=>F(i))  | 26
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map((_,i)=>F(i)) | 27

Примітка : довжина коду зворотного дзвінка F(i)не враховується.

[1..9]

[1..9]

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

Оптимізація без лічильника

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

Примітка : довжина коду зворотного дзвінка F()не враховується.


Не повинно 2**26бути 2**29?
Shaggy

@Shaggy Heck. Гарний улов!
Арнольд

Я не хотів редагувати в собі, тому що у мене сліпота коду! : D
Shaggy

Використовуючи .keys(), вам не потрібна лямбда:[...Array(10).keys()].map(do_something_with)
довгий лазуріт

@ long-lazuli Якщо вам не потрібна лямбда і просто хочете асортимент, то вам, мабуть, і карта не потрібна ...
Arnauld

4

Руйнування завдань

ES6 вводить новий синтаксис для деструктуризації завдань, тобто розрізання значення на шматки та присвоєння кожному фрагменту іншій змінній. Ось кілька прикладів:

Рядки та масиви

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

Об'єкти

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

Ці призначення можна також використовувати у параметрах функції:

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

Ще один спосіб уникнути return

Ви знаєте, що ви повинні використовувати eval для функцій зі стрілками з декількома операторами та поверненням . У деяких незвичайних випадках ви можете заощадити більше, використовуючи внутрішню функцію.

Я кажу незвично тому, що

  1. Повернений результат не повинен бути останнім виразом, оціненим у циклі

  2. Перед циклом повинно бути (принаймні) 2 різних ініціалізації

У цьому випадку ви можете використовувати внутрішню підфункцію без повернення, маючи одне з початкових значень, передане як параметр.

Приклад Знайдіть зворотну суму функції exp для значень в діапазоні від a до b.

Довгий шлях - 55 байт

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

З eval - 54 байти

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

З внутрішньою функцією - 53 байти

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

Зауважте, що без вимоги нижньої межі діапазону aя можу об'єднати ініціалізації i і r і версія eval коротша.


У вашому зразку зберігати не потрібноa
l4m2

@ l4m2 Я не можу зрозуміти вашу думку, допоможіть, будь ласка ...
edc65

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

@ l4m2 uh справа, return a/rбуде кращим прикладом
edc65

1
eval все ще краще, (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")і в цьому випадку(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon

4

Використання синтаксису каррі для діадичної та рекурсивної функцій

Діадичні функції

Кожен раз, коли функція бере рівно два аргументи без значень за замовчуванням, використання синтаксису currying зберігає один байт.

Раніше

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

Подзвонив с f(a,b)

Після

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

Подзвонив с f(a)(b)

Примітка : Ця публікація в Meta підтверджує дійсність цього синтаксису.

Рекурсивні функції

Використання синтаксису currying може також зберегти деякі байти, коли рекурсивна функція приймає кілька аргументів, але потрібно лише оновити деякі з них між кожною ітерацією.

Приклад

Наступна функція обчислює суму всіх цілих чисел у діапазоні [a,b]:

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

Оскільки aзалишається незмінним протягом усього процесу, ми можемо зберегти 3 байти, використовуючи:

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

Примітка : Як зауважив Ніл у коментарях, той факт, що аргумент не передається явно рекурсивній функції, не означає, що його слід вважати незмінним. При необхідності, можна змінити aв коді функції з a++, a--або будь-який інший аналогічний синтаксис.


Останній приклад можна записати як a=>F=b=>a>b?0:a+++F(b), змінюючи aдля кожного рекурсивного дзвінка. У цьому випадку це не допомагає, але може зберегти байти у випадках, у яких більше аргументів.
Ніл

Хе, я просто думав над тим, як написати підказку для цього :-)
ETHproductions

4

Функція тестування первинності

Наступні 28-байтові функції повертаються trueдля простих чисел і falseдля не-простих чисел:

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

Це можна легко змінити для обчислення інших речей. Наприклад, ця 39-байтна функція підраховує кількість простих чисел чи менше числу:

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

Якщо у вас вже є змінна, nяку ви хочете перевірити на первинність, функцію примітивності можна досить спростити:

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

Як це працює

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.

Примітка. Це не вдасться з помилкою "занадто багато рекурсії" при виклику з досить великим входом, наприклад, 12345. Ви можете обійти це за допомогою циклу:

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

1
Але не вдається з занадто великою рекурсією для введення всього 12345
edc65

x==1можливо, може бути x<2для економії.
CalculatorFeline

@CalculatorFeline Спасибі, але тоді вона не для 1чи 0(бо xбуло б 0або -1, відповідно)
ETHproductions

Може бути корисним у певних випадках. Також !~-xдля -0 байт.
CalculatorFeline

3

Array#concat() і оператор розповсюдження

Це багато в чому залежить від ситуації.


Поєднання декількох масивів.

Віддайте перевагу функціям concat, крім клонування.

0 байтів збережено

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

3 байти марно

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

Збережено 3 байти

a.concat()
[...a]

Збережено 6 байт

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

Краще використовувати вже існуючий масив для Array#concat().

Легко збережено 4 байти

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

3

Повернути проміжний результат

Ви знаєте, що за допомогою оператора кома ви можете виконати послідовність виразів, повертаючи останнє значення. Але зловживаючи синтаксисом буквального масиву, ви можете повернути будь-яке проміжне значення. Це корисно, наприклад, у .map ().

// 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
Згадайте, звичайно, що .join('')може бути.join``
Cyoce

3

Встановити параметри за замовчуванням функції

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

Цей дійсно корисний ...

Однак не забудьте зрозуміти, що щось на кшталт _=>_||'asdf'коротше, коли ви передаєте лише один (корисний) аргумент функції.


1
Зауважу, що використання АБО _=>_||'asdf'зазвичай у більшості випадків коротше
Пуховик

@Downgoat Зауважу, що це повертається "asdf"для введення ""(порожній рядок).
ETHproductions

2
Зауважте, що за замовчуванням оцінюється кожен раз, коли аргумент був би undefined, навіть якщо ви явно передаєте це значення. Наприклад, [...Array(n)].map((a,b,c)=>b)завжди передається undefinedдля a, і тому ви можете надати для нього значення за замовчуванням (хоча і не з точки зору b).
Ніл

3

Використовуйте evalзамість дужок для функцій стрілок

Функції стрілок є приголомшливими. Вони приймають форму x=>y, де xє аргументом і yє поверненим значенням. Однак якщо вам потрібно використовувати керуючу структуру, таку як while, вам доведеться поставити дужки, наприклад =>{while(){};return}. Однак ми можемо обійти це; на щастя, evalфункція приймає рядок, оцінює цей рядок як JS-код і повертає останнє оцінене вираз . Наприклад, порівняйте ці два:

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

Ми можемо використовувати розширення цієї концепції для подальшого скорочення нашого коду: в очах eval, контрольні структури також повертають останнє оцінене вираження. Наприклад:

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

3

Логічні операції з гольфу в ES6

"GLOE (S6)"

Загальна логіка

Скажіть, ви створили заяви sі t. Перевірте, чи можете ви використовувати будь-яку з наведених нижче замін:

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

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

(Вони не можуть працювати , якщо порядок не так, то є +і *мають більш низький пріоритет , ніж порядок ||і &&зробити.)

Також ось кілька зручних логічних виразів:

  • Або є, sабо tправда / XOR:s^t
  • sі tмають однакове значення істини: !s^tабоs==t

Логіка масиву

Усі учасники aвиконують умову 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)

Щонайменше один член aвідповідає умові 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)

Ні члени aзадовольняють умові p: !a.some(p).

Елемент eіснує у масиві 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має НЕ існує в масиві a:

!a.includes(e)
!~a.indexOf(e)
a.every(t=>t!=e)
!a.filter(t=>t==e).length
for(i=0,c=1;i<a.length;c*=e!=a[i++]);

Я взагалі використовую &&і ||як, x?y:xі x?x:yвідповідно. Але я бачу, як це було б корисно в більш логічних програмах. Єдиною проблемою +було б те, що, наприклад, 3і -3обидва є правдою, але 3+-3це не так.
ETHproductions

@ETHproductions Ах, ви праві; це крайній випадок. -може також працювати, якби s != t.
Conor O'Brien

a.filter(t=>t==e).length==a.lengthневірно. Це повинно бути!a.filter(t=>t==e).length
ETHproductions

@ETHproductions прямо ти!
Conor O'Brien

3

Скоротіть повторні виклики функцій

Якщо у вас неодноразові дзвінки до функції з іменем з довгим іменем, наприклад, маніпулювання полотном:

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

Традиційним способом скоротити це буде псевдонім назви функції:

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

Якщо у вас достатньо дзвінків, кращим способом є створення функції, яка виконує цю роботу для вас:

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

Якщо більшість функціональних викликів є ланцюговими, ви можете змусити функцію повернутися сама, що дозволяє скоротити два байти від кожного наступного дзвінка:

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

Приклад використання: 1 , 2


1
ви можете скоротити з оператором прив’язки :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Пуховик

@Downgoat Спасибі, які браузери це підтримують? (Крім того, з того, що я бачив, що буде помилка під час другого дзвінка, оскільки c.lineToвона, природно, не повертається)
ETHproductions

Ви повинні протерти це через вавило, оскільки це особливість ES7
Пуховик

3

Оператор зв’язування ::

Оператор прив'язки може бути використаний для скорочення байтів для повторних функцій:

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

Крім того, якщо ви хочете використовувати функцію з іншим, thisнаприклад:

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

Уникання коми після зберігання великої кількості даних

Якщо у вас є багато даних (тобто індексів, символів,…), які потрібно зберігати в масиві, можливо, вам буде краще залишити всі коми подалі. Найкраще це працює, якщо кожен фрагмент даних має однакову довжину рядка, 1 очевидно є оптимальним.

43 байт (базовий рівень)

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

34 байт (без коми)

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

Якщо ви готові змінити доступ до масиву , ви можете зменшити це ще більше, зберігаючи такі самі значення, як:

27 байт (ті самі дані, змінюється лише доступ до масиву)

a=[..."376189452543276543"]

Чому виділяється лише останній блок?
CalculatorFeline

@CalculatorFeline Дякую, виправлено.
Чіру
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.