Поради щодо гольфу в CJam


43

CJam - натхненна GolfScript мова для гри в гольф, створена користувачем aditsu користувача PPCG .

Отже, у поєднанні з іншими підказками, що стосуються конкретних мов:

Які загальні поради щодо гольфу в CJam? Будь ласка, опублікуйте одну пораду за кожну відповідь.


4
Дивіться також Поради щодо гольфу в GolfScript ; мови досить схожі, що багато хитрощів можна адаптувати будь-яким способом.
Ільмарі Каронен

2
@IlmariKaronen Переглянувши відповіді на це питання, я б сказав, що лише половина з них застосовується до CJam, через синтаксичні або логічні відмінності в мовах.
Оптимізатор

Відповіді:


23

Правильний модуль для від’ємних чисел

Часто дратує, що результат модульної операції дає такий самий знак, як і перший операнд. Наприклад, -5 3%дає -2замість 1. Найчастіше ви хочете останнього. Наївна фіксація полягає в застосуванні модуля, додаванні дільника один раз і застосуванні модуля знову:

3%3+3%

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

3,=

Застосовується -5, це дає, 1як очікувалося. І це лише на один байт довше вбудованого %!

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

32,=
31&

Для особливого випадку 65536 == 2^16інший байт можна зберегти, використовуючи поведінку загортання типу символу:

ci

13

Натискання об'єднаних діапазонів символів

  • Рядок, що містить усі цифри, "0123456789"може бути записаний як

    A,s
    
  • Прописні літери ASCII ( A-Z) можна натиснути як

    '[,65>
    

    яка генерує рядок усіх символів до Z , потім відкидає перші 65 (до @ ).

  • Усі літери ASCII ( A-Za-z) можна висувати як

    '[,65>_el+
    

    яка працює, як описано вище, потім створює копію, перетворює на малі регістри та додає.

    Але є коротший спосіб зробити це!

    Тоді часто недооцінений ^оператор (симетричні відмінності для списків) дозволяє створювати однакові діапазони, зберігаючи три байти:

    '[,_el^
    

    '[,створює діапазон усіх символів ASCII до Z , _elстворює малу копію та ^зберігає лише символи обох рядків, які відображаються в одній, але не в обох.

    Оскільки всі літери в першому рядку є великими літерами, всі в другому - малими літерами, а всі небуквенні символи - в обох рядках, результат - у рядку літер.

  • Алфавіт RFC 1642 Base64 ( A-Za-z0-9+/) можна натиснути за допомогою вищезазначеної методики та додавши літери:

    '[,_el^A,s+"+/"+
    

    Не менш короткий спосіб натискання на цей рядок використовує виключно симетричні відмінності:

    "+,/0:[a{A0":,:^
    

    Як ми можемо знайти рядок на початку?

    Всі використовувані діапазони символів ( A-Z, a-z, 0-9, +, /) може бути висунутий в якості симетричної різниці в межах , які починаються на нульовий байт, а саме 'A,'[,^, 'a,'{,^, '0,':,^, '+,',,^і '/,'0,^.

    Тому виконання :,:^на "A[a{):+,/0"ньому висуне бажані символи, але не в потрібному порядку.

    Як ми знаходимо правильний порядок? Жорстока сила на допомогу! Програма

    '[,_el^A,s+"+/"+:T;"0:A[a{+,/0"e!{:,:^T=}=
    

    ітераціює над усіма можливими перестановками рядка, застосовує :,:^та порівнює результат із потрібним результатом ( постійна посилання ).

  • Використовуваний алфавіт radix-64, наприклад, crypt ( .-9A-Za-z), може бути сформований за допомогою описаного вище способу:

    ".:A[a{":,:^
    

    Це найкоротший метод, який я знаю.

    Оскільки всі символи потрібного виводу є в порядку ASCII, ітерація над перестановками не потрібна.

  • Не всі об'єднані діапазони символів можна висунути в потрібному порядку за допомогою :,:^.

    Наприклад, діапазон 0-9A-Za-z;-?не можна висунути, виконавши :,:^будь-яку перестановку "0:A[a{;@".

    Однак ми можемо знайти поворотну варіацію потрібного рядка, яка може, використовуючи код

    A,'[,_el^'@,59>]s2*:T;"0:A[a{;@"e!{:,:^T\#:I)}=Ip
    

    який надрукує ( постійна посилання ) наступного:

    10
    0:@[a{A;
    

    Це означає що

    "0:@[a{A;":,:^Am>
    

    має такий же ефект, як і

    A,'[,_el^'@,59>]s
    

    який можна використовувати лише з порожнім стеком без попереднього а [.


11

Уникати {…} {…}?

Припустимо, у вас є ціле число. Якщо вона непарна, потрібно помножити її на 3 і додати 1; інакше ви хочете поділити його на 2.

"Нормальний" випадок if / else виглядатиме так:

_2%{3*)}{2/}?

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

_2%1$3*)@2/?

Це на один байт коротше.


Блок-? з порожнім, якщо заява завжди не йде. Наприклад,

{}{2/}?

на два байти довше, ніж

{2/}|

Якщо замість цього у вас є

{2/}{}?

і те, що ви перевіряєте, це невід'ємне ціле число, ви можете це зробити

g)/

Нові {}&і {}|зручні, але іноді проблематичні, якщо ви не можете захарастити стек.

Все-таки у випадку

_{…}{;}?

ви можете використовувати тимчасову змінну замість цього:

:T{T…}&

1
!)/і g)/коротші у прикладах.
jimmy23013

11

Переключити заяви

У CJam немає тверджень про переключення. Вкладено, якщо операції працюють так само добре, але {{}{}?}{}?вже 12 байт ...

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

Наприклад, якщо ми хочемо виконати, code0якщо ціле число стека дорівнює 0 , code1якщо воно дорівнює 1 , і code2якщо воно 2 , ми можемо використовувати

_{({code2}{code1}?}{;code0}?

або

[{code0}{code1}{code2}]=~

або

"code0 code1 code2"S/=~

S/розбиває рядок на ["code0" "code1" "code2"], =витягує відповідний фрагмент і ~оцінює код.

Клацніть тут, щоб побачити оператори переключення в дії.

Нарешті, як це запропонували @ jimmy23013 та @RetoKoradi, у деяких випадках ми можемо ще більше скоротити вимикач. Скажіть code0, code1і code2мають довжини L 0 , L 1 і L 2 відповідно.

Якщо L 0 = L 1 ≥ L 2

"code0code1code2"L/=~

може бути використаний замість, де Lце L 0 . Замість того, щоб розщеплюватися на роздільнику, тут /розбивається струна на шматки однакової довжини.

Якщо L 0 ≥ L 1 ≥ L 2 ≥ L 0 - 1 ,

"cccooodddeee012">3%~

можна використовувати замість цього. >видаляє 0, 1 або 2 елемента з початку рядка і 3%витягує кожен третій елемент (починаючи з першого).


Для останнього прикладу, чи має це перевага "code0code1code2"5/=~? Здається мені набагато простіше, і це однакова довжина.
Рето Кораді

@RetoKoradi Якщо всі фрагменти мають однакову довжину, переваги немає. Для різної довжини ваш метод може бути як коротшим, так і довшим, ніж метод модуля.
Денніс

11

Гольф загальних значень масиву та рядків

Існують певні короткі масиви або рядки, які виникають раз у раз, наприклад для ініціалізації сіток. Наївно це може коштувати 4 або більше байт, тому варто шукати операції над вбудованими значеннями, які дадуть такий же результат. Особливо корисне перетворення часто.

  • [0 1]можна записати як 2,.
  • [1 0]можна записати як YYb(тобто 2 у двійковій).
  • [1 1]можна записати як ZYb(тобто 3 у двійковій).
  • Матрицю [[0 1] [1 0]]можна записати як 2e!.
  • [LL] можна записати як SS/(розділення одного простору на пробіли).
  • "\"\""можна записати як L`.
  • "{}"можна записати як {}s.

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

  • "[{<()>}]"можна записати як {<()>}a`.
  • "()<>[]{}"можна записати як {<()>}a`$.

Особливо хитрість базового перетворення може бути корисною, щоб мати на увазі деякі випадки незрозумілості, які виникають раз у раз. Наприклад, [3 2]було б E4b(14 в базі 4).

У більш рідкісних випадках ви навіть можете скористатися оператором факторизації mf. Наприклад , [2 7]є Emf.

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


10

Очищення стека

Якщо ви просто хочете очистити весь стек, загорніть його в масив і виведіть його:

];

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

]W=

(Дякую Оптимізатору, який показав це мені днями.)

Звичайно, якщо на стеку \;є лише два елементи, це коротше.


\;відображатиметься лише під елементом нижче TOS. Ви мали на увазі ;;?
CalculatorFeline

1
@CalculatorFeline друга половина відповіді стосується очищення всього, крім TOS.
Мартін Ендер

9

e і повноважень десяти

Як і в багатьох інших мовах, ви можете писати 1e3замість 1000CJam.

Це працює і для нецілих баз, і навіть для не цілих показників. Наприклад, 1.23e2штовхає 123,0 і 1e.5штовхає 3,1622776601683795 (квадратний корінь з 10 ).

Що не відразу очевидно - 1e3це насправді два жетони:

  • 1висуває ціле число 1 на стек.

  • e3помножує його на 1000 .

Чому це важливо?

  • Ви можете зателефонувати e<numeric literal>на те, що вже є у стеці.

    2 3 + e3 e# Pushes 5000.
    
  • Можна зіставити карту e<numeric literal>через масив.

    5 , :e3  e# Pushes [0 1000 2000 3000 4000].
    

9

Евклідові норми

Прямий спосіб обчислення евклідової норми вектора, тобто квадратний корінь суми квадратів його елементів, є

2f#:+mq

Однак існує набагато коротший шлях.

mh, оператор гіпотенузи, вискакує два цілих числа a і b зі стека і виштовхує sqrt (a 2 + b 2 ) .

Якщо у нас є вектор x: = [x 1 … x n ], n> 1 на стеці :mh(зменшити за допомогою гіпотенузи), вийде таке:

  • Спочатку х 1 і х 2 висуваються і mhвиконується, залишаючи sqrt (x 1 2 + x 2 2 ) на стеці.

  • Потім x 3 висувається і mhвиконується знову, залишаючи
    sqrt (sqrt (x 1 2 + x 2 2 ) 2 + x 3 2 ) = sqrt (x 1 2 + x 2 2 + x 3 2 ) на стеку.

  • Після оброблення x n нам залишається sqrt (x 1 2 +… x n 2 ) , евклідова норма x .

Якщо n = 1 і x 1 <0 , наведений вище код дасть неправильний результат. :mhzпрацює беззастережно. (Дякую @ MartinBüttner за те, що вказав на це.)

Я вперше використав цю хитрість у цій відповіді .


2
Звичайно, це має значення для чисельного аналізу вашої програми ...
Пітер Тейлор

8

Перетворити з бази n зі списком чисел, більшим за n

CJam перетворює список у число з такою формулою: A 0 * n l + A 1 * n l-1 + A 2 * n l-2 + A l * n 0 (з відмінністю n). nє базовою і lє довжиною списку. Це означає, що A i може бути будь-яким цілим числом, яке не повинно бути в діапазоні [0,n).

Деякі приклади:

  • 0bвитягує останній елемент і кидає його на ціле число. Працює як W=iі зберігає байт, якщо він не був цілим числом. Але все інше у списку також має бути вміщеним у ціле число.
  • 1bповертає суму. Працює як :i:+і зберігає два байти, якщо вони не були цілими числами. Він також працює з порожніми списками, а :+ні.
  • [i{_1&9 32?_@\m2/}16*;]W%:cперетворює символ у рядок закінчень рядків та вкладок, які можна перетворити назад 2bc. Однак функцію кодування непросто переграти в програмі з кодом-гольф. Але цього вам зазвичай не потрібно.
  • Ви можете використовувати наступний код для перетворення рядка в символи Unicode не в 16 бітах, які можна перетворити назад 2A#b128b:c. (Пояснення будуть додані пізніше. Або, можливо, я напишу нову версію пізніше.)

    128b2A#b         " Convert to base 1024. ";
    W%2/)W%\:+       " Convert to two base 1024 digit groups. ";
    [0X@
    {
      _54+
      @I+_Am>@\-
      _Am<@+ 0@-@1^
    }fI
    ]);)
    @\+[~+]2A#b_2G#<!{2A#b}*
    \W%+:c
    

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


8

Використання $як потрійний, якщо

Якщо ви не заперечуєте протікання пам’яті, тобто, залишаючи невикористані елементи на стеку, який згодом буде очищено ];, оператор копіювання $може стати зручною заміною потрійному оператору ?.

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

Якщо у вас є A B Cстек, ви можете виконати

!$

замість

\@?

копіювати, Bякщо Cце правда та Aінше.

Якщо Cє фактичним булевим ( 0або 1), його можна виконати

$

замість

@@?

копіювати, Aякщо Cце правда та Bінше.


Заднім числом це досить очевидний трюк, але я раніше про це не думав. Я вперше використав це у цій відповіді .
Денніс

7

Карта для вкладених списків

Скажіть, у вас вкладений список, як матриця:

[[0 1 2][3 4 5][6 7 8]]

Або масив рядків:

["foo""bar"]

І ви хочете зіставити блок на вкладений рівень (тобто застосувати його до кожного числа чи кожного символу). Наївне рішення - це вкладене %:

{{...}%}%

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

{...}f%

Зберігає два байти.

Ще один акуратний трюк зробити щось подібне for (i=0; i<5; ++i) for (j=0; j<5; ++j) {...}- це

5,_f{f{...}}

Зовнішній fбуде відображатися на перший діапазон, подаючи другий діапазон як додатковий параметр. Але тепер, якщо ви fзнову використовуєте , лише верхній елемент стека є масивом, тому ви fвідображаєте внутрішній блок на нього, подаючи зовнішню "змінну ітерації" як додатковий параметр. Це означає, що внутрішній блок працює з iі jна стеку.

Це має таку ж кількість символів, що й просто відображення блоку на декартовому продукті (хоча останній стає коротшим, якщо вам потрібні пари як масиви):

5,_m*{~...}%

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

Дякую Деннісу, що показав мені цю хитрість.

0.6.4 Оновлення

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

{:x}%
{x}f%
::x

Це насправді не допомагає зі складанням блоків на вкладені списки.

Що стосується застосувань блоків або операторів до декартового продукту, це також скоротилося і для блоків, і для операторів:

5,_f{f{...}}
5,_ff{...}

5,_f{fx}
5,_ffx

Приємно те, що тепер ви можете їх гніздо. Таким чином, ви можете так само легко застосувати оператора до третього рівня внизу списку:

:::x

Або блок з деякою хитрістю:

{...}ff%

Чудове оновлення. Але досі немає f~...
jimmy23013

@ user23013 fочікує, що двійковий оператор не ~є одинаковим; ти, можливо, захотів :~? Також ми можемо обговорити це у чаті
aditsu

Чи пропускаю я щось про це оновлення 0.6.4? Я все ще отримую повідомлення про помилки, виконуючи такі хитрощі, як-от Unhandled char after ':': :( посилання )
Runer112,

2
@ Runer112 працює для мене. Переконайтесь, що ви перезавантажилися належним чином (тобто не з кешу). Залежно від вашого браузера Ctrl + F5 повинен працювати.
Мартін Ендер

@ MartinBüttner Це справді було викликано дурним кешуванням. Дякую.
Runer112

7

Векторизовані оператори для мистецтва ASCII

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

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

  • .e<

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

    Оскільки пробіл має нижчу кодову точку, ніж усі інші символи для друку ASCII, .e<його можна використовувати для "стирання" частин згенерованого шаблону:

    "\/\/\/\/\/" "    " .e<
    
    e# This pushes "    \/\/\/".
    

    Для повного прикладу дивіться мою відповідь на « Мені хочу соти» .

  • .e>

    Максимальний оператор e>працює як мінімальний оператор з протилежним результатом.

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

    [[" " " " " " " "] [" " " " " " " "]][["+" "" "-" ""]["" "*" "" "/"]] ..e>
    
    e# This pushes [["+" " " "-" " "] [" " "*" " " "/"]].
    

    Для повного прикладу дивіться мою відповідь на Seven Slash Display .

  • .e&

    Логічний оператор AND e&висуває його лівий аргумент, якщо він хибний, а його правий аргумент - в іншому випадку.

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

    "################" " * * * *" .e&
    
    e# This pushes " * * * *########".
    

    Для повного прикладу дивіться мою відповідь на Друк американського прапора! .

  • .e|

    Логічний оператор АБО e|може використовуватися як вище, із зворотним порядком аргументів:

    " * * * *" "################" .e|
    
    e# This pushes " * * * *########".
    

6

Використовуйте, &щоб перевірити, чи є предмет у списку

Для

1 [1 2 3] #W>
1 [1 2 3] #)

Можна використовувати

1 [1 2 3] &,
1 [1 2 3] &

натомість, який повертає 0/1 та truthy / falsey відповідно.


6

z і не прямокутні масиви

Оператор zip zпереміщує рядки та стовпці двовимірного 1 масиву A , елементи якого також можуть бути ітерабельними.

Для не прямокутних масивів - на відміну від вбудованих zipфункцій у, наприклад, Python (обрізає рядки на однакову довжину) або Ruby (прокладки рядків nil) - CJam просто перетворює стовпці масиву в рядки, ігноруючи їх довжину і прогалини.

Наприклад, блискавка масиву

[
  [1]
  [2 4]
  [3 5 6]
]

еквівалентно застібці масиву

[
  [1 4 6]
  [2 5]
  [3]
]

або масив

[
  [1]
  [2 4 6]
  [3 5]
]

як підштовхують усі три дії

[
  [1 2 3]
  [4 5]
  [6]
]

на стеку.

Хоча це означає, що zце не інволюція (що може бути корисно в деяких випадках), але у неї є кілька застосувань.

Наприклад:

  • Ми можемо вирівняти стовпці масиву до вершини (тобто перетворити перший масив у другий), двічі перетягуючи:

    zz
    
  • Незначні модифікації вищевказаного методу можуть бути використані для подібних проблем.

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

    W%zzW%
    
  • Враховуючи масив рядків, ми можемо обчислити довжину найдовшого рядка таким чином:

    :,:e>
    

    Однак, блискавки та обчислення кількості рядків результату, ми можемо зберегти три байти:

    z,
    

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


1
Просто інший спосіб візуалізації того ж самого, але для мене поведінка набагато логічніше, якщо я малюю zперетворення стовпців у рядки, тоді як порожні значення пропускаються. У прикладі перший стовпець на вході дорівнює 1, 2, 3, другий стовпець - 4, 5 (порожнє місце пропускається), а третій стовпчик - 6. Це рядки результату.
Рето Коради

@RetoKoradi Це набагато кращий спосіб описати це.
Денніс

6

Винятки

Усі винятки є смертельними для CJam. Оскільки вихід за STDERR за замовчуванням ігнорується , ми можемо використовувати це на нашу користь.

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

Ось кілька випадків використання:

  • Очищення невеликого стека

    Для очищення стека, який містить два елементи, @можна використовувати. @намагається спливати три елементи стека, але не виходить після появи другого.

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

    Дивіться це в дії тут .

  • Видалення двох або трьох елементів зі стека

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

    Щоб вивести два елементи, bпрацює, якщо один з них є символом або жоден з них не є цілим числом.

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

  • Вихід із циклу

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

    Приклад, що стосується арифметики, дивіться тут .

    Приклад із застосуванням рядків дивіться тут .

  • Умовне виконання

    Якщо вихідний код не повинен виконуватись для певних типів введення, ми іноді можемо використовувати оператора, який не вводить такого типу введення.

    Наприклад, iвийде з ладу для рядків, які не оцінюються до цілого числа, і ewне вдасться для рядків довжиною 0 або 1.

    Дивіться це в дії тут .


5

Макс / хв з масиву

Ось один для початку!

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

Отже, якщо масив знаходиться в змінній A

A$W=

є максимальним і

A$0=

є мінімальним.

Отримати обидва одночасно теж можливо

A$)\0=

Це може здатися очевидним після прочитання, але перша спроба будь-кого має тенденцію йти до використання e<або e>через ітерацію через масив, що йде як

A{e<}*

що на 2 байти довше і навіть довше, якщо ви хочете як макс, так і хв.


Звичайно, якщо ви не заперечуєте проти решти масиву, що залишився в стеці, ви можете фактично використовувати (і )замість 0=і W=.
Мартін Ендер

Зараз є :e<і:e>
aditsu

@aditsu Хоча вони не коротші, ніж наконечник вище.
Оптимізатор

5

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

Якщо вам потрібна дуже велика, але довільна кількість, зазвичай ви або використовуєте наукові позначення, як, 9e9або піднімаєте одну з великих вбудованих змінних на подібну потужність, як KK#. Однак якщо вам не байдуже, що таке фактичне число, і воно не повинно бути послідовно однаковим (наприклад, як верхня межа для випадкового числа), ви можете зробити це в два байти, використовуючи

es

замість цього. Це дає поточну мітку часу в мілісекундах і знаходиться в порядку 10 12


3
Також зауважте, що якщо вам потрібно велике довільне число, а ви хочете разом відмовитись від додатного числа, ви можете використовувати e9.
jimmy23013

5

Перевірка, що два рядки / масиви не рівні

Іноді ви хочете, щоб значення truthy, коли два рядки або масиви не рівні, і хибне значення, якщо вони є. Очевидне рішення - два байти:

=!

Перевірте рівність і переверніть результат. Однак за певних умов можна використовувати

#

Коли #застосовується до двох масивів, він фактично шукає другий масив як підмагістраль першого (і дає вам індекс, з якого починається підматриця). Отже, якщо два масиви однакові, підмножина знайдеться на самому початку та видасть 0, що є помилковим. Але якщо другого масиву не вдасться знайти, він видасть, -1що є правдою.

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

"abc""ab"#

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


5

c і 16-бітні цілі числа

Щоб додати (або відняти) неподписані 16-бітні цілі числа при правильному обгортанні, ви можете використовувати +65536%або +2G#%.

Однак,

+ci

набагато коротше. Символи обертаються на рівні 65536 , тому відтворення символів ( c), а потім Довгого ( i) має аналогічну дію 65536%, з додатковою перевагою, що результат не буде негативним.

Цей же трюк можна використовувати для натискання 65535 :

Wci

4

Комплекти живлення

Скажіть, у вас є масив, і ви хочете масив із усіма можливими підмножинами цього масиву. Трюк полягає в тому, щоб почати з порожнього масиву, а потім для кожного елемента дублювати наявні підмножини та додати до них новий елемент (зберігаючи попередній результат там, де елемент не був доданий ). Зауважте, що вам потрібно ініціалізувати стек з базовим регістром, тобто масивом, що містить лише порожній масив: Це може виглядати приблизно так:

[1 2 3 4 5]La\{1$f++}/

Приємно в тому, що ви можете негайно запустити деякі підрахунки на підмножину, потенційно без доданих символів. Скажіть, що вам потрібні продукти всіх підмножин. У цьому випадку базовий випадок - це масив, що містить 1, і на кожному кроці ви берете попередній список можливих продуктів, дублюєте його та множите все у дублікаті на новий елемент:

[1 2 3 4 5]1a\{1$f*+}/

4

Перевірте, чи всі елементи в списку однакові

Я думаю, що це також варто згадати. Використання:

)-

Повертає truthy, якщо не все те саме, або порожній список, якщо всі однакові. Помилки, якщо список порожній.

Якщо вилучений елемент може бути самим масивом (або рядком):

)a-

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

_|,(

4

0= для струн

Щоб отримати перший елемент масиву, ви повинні використовувати 0=(або (, якщо ви не проти залишити решту масиву в стеку).

Однак, якщо цей масив є рядком, достатньо надати символів символу.

Приклад

"xyz"c e# Pushes 'x.

Я не бачу, чому CJam не просто дозволяє cвитягнути перший елемент будь-якого масиву, який був би більш корисним і послідовним.
Esolanging Fruit

4

Обертання масиву (або стека) на один блок вліво

CJam має лівий оператор обертанняm< , який, як правило, слід використовувати для обертання масиву довільної кількості одиниць ліворуч.

У деяких випадках ви також можете використовувати (+для зміни та додавання:

[1 2 3]       (+ e# Pushes [2 3 1].
[[1] [2] [3]] (+ e# Pushes [[2] [3] 1].

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

Крім того, якщо ви хочете скинути повернутий масив на стек, ви можете використовувати :\(зменшити, заміняючи) беззастережно:

[1 2 3]       :\ e# Pushes 2 3 1.
[[1] [2] [3]] :\ e# Pushes [2] [3] [1].

Поки у вас немає відкритого [, цей трюк також можна використовувати для обертання всього стека, тобто для підведення найнижчого елемента стека до верху:

]:\

3

Друк списку та очищення стека

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

123 "waste" ["a" "b" "rty" "print" "me" "please"]

Тепер вам цікаво друкувати лише останній список, так що і ви

S*]W=

який виводить

a b rty print me please

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

Це можна гольфу далі!

p];

Це на 2 байти коротше !

і якщо у вас є лише один предмет у стеці, крім списку, його ще коротше!

p;

Краса pполягає в тому, що він видаляє найвищий елемент із стека, впорядковує його (також додає новий рядок наприкінці) та друкує STDOUT миттєво, не чекаючи завершення коду.

Отже, наведений вище код виведе

["a" "b" "rty" "print" "me" "please"]

що є точним поданням списку, коли він був у стеці!


3

Декартові вироби або всі можливі комбінації двох або більше наборів

CJam має вбудований декартовий продукт-калькулятор, m*який бере два верхні масиви / рядки на стек і створює з нього всі можливі пари. Наприклад

[1 2 3 4]"abc"m*

листя

[[1 'a] [1 'b] [1 'c] [2 'a] [2 'b] [2 'c] [3 'a] [3 'b] [3 'c] [4 'a] [4 'b] [4 'c]]

як стек

Але що робити, якщо вам потрібні всі можливі комбінації з більш ніж 2 списків / рядків. Ти m*цим багато разів користуєшся ? Наприклад

[1 2 3 4][5 6]"abc"m*m*

залишить наступне на стеці

[[1 [5 'a]] [1 [5 'b]] [1 [5 'c]] [1 [6 'a]] [1 [6 'b]] [1 [6 'c]] [2 [5 'a]] [2 [5 'b]] [2 [5 'c]] [2 [6 'a]] [2 [6 'b]] [2 [6 'c]] [3 [5 'a]] [3 [5 'b]] [3 [5 'c]] [3 [6 'a]] [3 [6 'b]] [3 [6 'c]] [4 [5 'a]] [4 [5 'b]] [4 [5 'c]] [4 [6 'a]] [4 [6 'b]] [4 [6 'c]]]

Зауважте, що товари все ще є парами, де одним із предметів є сама пара. Цього не очікується, і ми хочемо вирівняти комбінації.

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

[1 2 3 4][5 6]"abc"]{m*{(+}%}*

Це листя

[['a 5 1] ['b 5 1] ['c 5 1] ['a 6 1] ['b 6 1] ['c 6 1] ['a 5 2] ['b 5 2] ['c 5 2] ['a 6 2] ['b 6 2] ['c 6 2] ['a 5 3] ['b 5 3] ['c 5 3] ['a 6 3] ['b 6 3] ['c 6 3] ['a 5 4] ['b 5 4] ['c 5 4] ['a 6 4] ['b 6 4] ['c 6 4]]

на стек.

Хочете, щоб порядок підтримувався? , просто поміняйте місцями перед тим, як додати спливаючий елемент назад до масиву. тобто

{m*{(\+}%}*

Хочете лише перестановки?

{m*{(+$}%_&}*

Хочете лише унікальних елементів у комбінаціях?

{m*{(+_&}%}*

Це все, шановні. поки що .


1
Тепер ви також можете робити ]:m*:e_з будь-якою кількістю масивів
aditsu

3

Операція на струнах

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

Наприклад, якщо ви хочете отримати перші або останні кілька елементів у двовимірному масиві бітів, і вам не важливо повернутий тип, sA<зберігається байт від 0=A<або :+A<.

Або якщо ви хочете змінити деякі біти на вході, ви можете змінити рядок перед його оцінкою.

Або якщо ви отримали цю структуру і хочете перетворити її у простий список:

[[[[[[[[[1]2]3]4]5]6]7]8]9]

Ви можете зробити це з багатьма символами іншими способами:

[a{~)\}h;]W%

Але це може бути набагато коротше за допомогою рядків:

s:~

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

[`La`-~]

Або:

`']-~]

Якщо вам не потрібен інший масив, що містить безліч таких масивів.


Є e_зараз
aditsu

@aditsu Дивіться цю відповідь та коментар . Іноді sвсе-таки працює краще.
jimmy23013

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

3

Використання NзамістьLa

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

У багатьох випадках перед друком також потрібно додавати новий рядок після кожного елемента, який би був чимось на зразок Noабо N*.

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

Іноді Sтакож працює, якщо потрібно розділити вихід пробілами.

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


2

Розщеплення на одну або кілька подій

Скажіть, у вас є рядок, "abbcdbbfghbdbb"і ви хочете розділити йогоb

"abbcdbbfghbdbb"'b/

Це залишає на стеці:

["a" "" "cd" "" "fgh" "d" "" ""]

Помітили порожні рядки? Вони там, тому що двоє bбули разом, і між ними нічого не було. Часом ви хочете цього уникнути. Це можна зробити за допомогою

"abbcdbbfghbdbb"'b/La-

або фільтрування порожніх рядків

"abbcdbbfghbdbb"'b/{},

але це 3 зайвих байти.

Трохи менш відомий оператор для цього конкретного випадку використання %. Крім того, щоб робити мод та карту та розбивати на основі числа ( "abcd"2%= "ac"), %можна також розділити на рядки / масиви. Отже, для наведеного вище випадку використання:

"abbcdbbfghbdbb"'b%

виїде, залишить

["a" "cd" "fgh" "d"]

на стек.

Дякуємо @ @ user23013, що вказав на це в одній із моїх відповідей сьогодні.


Я думаю, що це має бути названо "також вивчити GolfScript", що має кращі приклади в документації.
jimmy23013

@ user23013, але ми ніколи не впевнені, що все схоже на GS, а що ні.
Оптимізатор

2

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

Ми маємо :xяк скорочення для {x}%та {x}*( або залежно від того, чи xє одинарним чи двійковим). На жаль, не існує еквівалентного оператора інфіксації, який би скорочувався {x}/. Однак дуже часто, коли ми це робимо {x}/, xнасправді є двійковим оператором, який неодноразово модифікує елемент, що лежить під стеком. Якщо це так, і вказаний елемент не є масивом, ми можемо зберегти байт, зловживаючи скласти / зменшити як foreach:

5 [1 2 3 4]{-}/  e# Gives -5
5 [1 2 3 4]+:-

Це працює, тому що складка завжди залишає перший елемент недоторканим. На жаль, він не зберігає байт, коли модифікований елемент є масивом, тому що додаючи його, його буде розгорнуто. Однак іноді вам пощастило, що ваш масив уже містить цей елемент на передній панелі, і в цьому випадку слід пам’ятати про зменшення (замість того, щоб видалити елемент вручну перед тим, як використовувати {}/на залишку).


2

друк та println

CJam має printоператор: o. Він працює, але стек друкується відразу після того, як весь код виконаний. Ви можете зупинити це, якщо очистите стек в кінці програми. Просто поставте це в кінці:

];

Для друку ви можете використовувати oNoабо p(працює як `oNo)


3
Існує більша різниця між oта p. pпочинається з перетворення елемента для друку в однозначне подання рядків. pє рівносильним виконанню ​`oNo.
Денніс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.