Советы по игре в гольф в CJam


43

CJam - это основанный на GolfScript язык стека для гольфа, созданный пользователем PPCG aditsu .

Итак, в духе других языковых советов вопросы:

Какие общие советы у вас есть для игры в гольф в 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
    

    который можно использовать только с пустым стеком, не добавляя a [.


11

Избегать {…} {…}?

Предположим, у вас есть целое число в стеке. Если это нечетно, вы хотите умножить его на 3 и добавить 1; в противном случае вы хотите разделить его на 2.

«Нормальный» оператор if / else будет выглядеть так:

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

Тем не менее, использование блоков, как правило, не подходит, так как {}{}уже добавляет четыре байта. ?также может использоваться для выбора одного из двух элементов в стеке:

_2%1$3*)@2/?

Это на один байт короче.


Блок-? с пустым оператором if всегда не допускается. Например,

{}{2/}?

на два байта длиннее

{2/}|

Если вместо этого у вас есть

{2/}{}?

и то, что вы проверяете, является неотрицательным целым числом, вы можете сделать

g)/

Новые {}&и {}|удобны, но иногда проблематичны, если вы не можете загромождать стек.

Тем не менее, в случае

_{…}{;}?

вместо этого вы можете использовать временную переменную:

:T{T…}&

1
!)/и g)/короче в примерах.
jimmy23013

11

Смена операторов

У CJam нет операторов переключения. Вложено, если операторы работают так же хорошо, но {{}{}?}{}?уже имеют длину 12 байт ...

Если мы можем преобразовать условие в небольшое неотрицательное целое число, мы можем преобразовать все операторы case в строку с разделителями и оценить соответствующий результат.

Например, если мы хотим выполнить, code0если целое число стека равно 0 , code1если оно равно 1 , и code2если оно равно 2 , мы можем использовать

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

или

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

или

"code0 code1 code2"S/=~

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

Нажмите здесь, чтобы увидеть операторы switch в действии.

Наконец, как предлагают @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(уменьшение на гипотенузу) достигнет следующего:

  • Сначала x 1 и x 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является длиной списка. Это означает, что я могу быть любым целым числом, которое не должно быть в диапазоне [0,n).

Некоторые примеры:

  • 0bизвлекает последний элемент и приводит его к целому числу. Работает как W=iи сохраняет байт, если он не был целым числом. Но все остальное в списке также должно быть в состоянии привести к целому числу.
  • 1bвозвращает сумму. Работает как :i:+и сохраняет два байта, если они не были целыми числами. Он также работает с пустыми списками, пока :+нет.
  • [i{_1&9 32?_@\m2/}16*;]W%:cпреобразует символ в строку с окончаниями строки и табуляцией, которые можно преобразовать обратно с помощью 2bc. Тем не менее, функцию кодирования нелегко использовать в программе code-golf. Но обычно вам это не нужно.
  • Вы можете использовать следующий код для преобразования строки в символы 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целых чисел, которые имеют разные значения мода 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) {...}IS

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<работы для пар строк, символов, массивов и целых чисел; он вытаскивает два предмета из стека и толкает нижнюю часть обратно.

    Поскольку пробел имеет более низкую кодовую точку, чем все другие печатаемые символы ASCII, .e<его можно использовать для «стирания» частей сгенерированного шаблона:

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

    Полный пример см. В моем ответе на « Мне нужны соты» .

  • .e>

    Оператор максимума e>работает как оператор минимума, с противоположным результатом.

    Опять же, из-за низкой кодовой точки пробела, .e>можно использовать для вставки шаблона печатаемых символов в блок пробелов:

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

    Полный пример см. В моем ответе на Seven Slash Display .

  • .e&

    Логический оператор AND e&выдвигает свой левый аргумент, если он ложный, и правый аргумент в противном случае.

    Если ни один из шаблонов не содержит ложных элементов, это можно использовать для наложения одного шаблона поверх другого:

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

    Для полного примера, см. Мой ответ на Печать американского флага! ,

  • .e|

    Логический оператор OR 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 и truey / falsey соответственно.


6

z и непрямоугольные массивы

Оператор почтового индекса zпереставляет строки и столбцы двумерный 1 массив А , чьи элементы могут быть также итерируемым.

Для непрямоугольных массивов - в отличие от встроенных 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

Проверка того, что две строки / массива не равны

Иногда вам нужно истинное значение, когда две строки или массивы не равны, и ложное значение, если они равны. Очевидное решение - два байта:

=!

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

#

Когда #применяется к двум массивам, он фактически ищет второй массив как подмассив первого (и дает вам индекс, с которого начинается подмассив). Таким образом, если два массива одинаковы, подмассив будет найден в самом начале и даст 0, что ложно. Но если второй массив не может быть найден, он выдаст -1правду.

Причина, по которой нам нужны некоторые дополнительные условия для двух массивов, заключается в том, что это также дает ложное значение, если второй массив является нетривиальным префиксом первого, например:

"abc""ab"#

дает, 0хотя строки не совпадают. Самое простое условие, которое исключает этот случай, если вы знаете, что оба массива будут одинаковой длины - в этом случае, если один является префиксом другого, вы знаете, что они равны. Но в определенных обстоятельствах могут быть более слабые условия, которые также являются достаточными. Например, если вы знаете, что строки отсортированы, префикс всегда будет первой строкой, а не второй.


5

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

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

Тем не мение,

+ci

намного короче. Символы переносятся вокруг 65536 , поэтому приведение к Character ( c), а затем к Long ( i) имеет аналогичный эффект 65536%с дополнительным преимуществом, что результат не будет отрицательным.

Тот же трюк можно использовать для нажатия на 65535 :

Wci

4

Наборы мощности

Скажем, у вас есть массив, и вы хотите массив со всеми возможными подмножествами этого массива. Хитрость заключается в том, чтобы начать с пустого массива, а затем для каждого элемента продублировать уже имеющиеся у вас подмножества и добавить в них новый элемент (сохраняя предыдущий результат, где элемент не был добавлен ). Обратите внимание, что вам нужно инициализировать стек с базовым регистром, то есть массивом, содержащим только пустой массив: это может выглядеть так:

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

Хорошая вещь в этом заключается в том, что вы можете сразу выполнить некоторые вычисления для подмножества, возможно, без добавления символов. Скажем, вы хотите продукты всех подмножеств. В этом случае базовый вариант представляет собой массив, содержащий 1, и на каждом шаге вы берете предыдущий список возможных продуктов, дублируете его и умножаете все в дубликате на новый элемент:

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

4

Проверьте, все ли элементы в списке одинаковы

Я думаю, что это также стоит упомянуть. Использование:

)-

Возвращает truey, если не все одинаковые, или пустой список, если все одинаковые. Ошибки, если список пуст.

В случае, если извлеченный элемент может быть сам массив (или строка):

)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 байта короче !

и если у вас есть только 1 предмет в стеке, кроме списка, он еще короче!

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

Используйте складывание / уменьшение как инфикс foreach

Мы имеем :xв качестве сокращения для {x}%и или {x}*(в зависимости от того, xявляется ли унарный или двоичный). К сожалению, нет эквивалентного инфиксного оператора для сокращения {x}/. Однако, очень часто, когда мы это делаем {x}/, xна самом деле это бинарный оператор, который многократно изменяет элемент, лежащий внизу в стеке. Если это так, и указанный элемент не является массивом, мы можем сохранить байт, использовав сложение / уменьшение как foreach:

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

Это работает, потому что сгиб всегда оставляет первый элемент без изменений. К сожалению, он не сохраняет байт, когда модифицированный элемент является массивом, потому что его добавление развернет его. Однако, иногда вам повезло, что ваш массив уже содержит этот элемент спереди, и в этом случае следует помнить о сокращении (вместо того, чтобы вручную удалять элемент перед использованием {}/в оставшейся части).


2

печатать и печатать

CJam имеет printоператор: o. Это работает, но стек печатает сразу после выполнения всего кода. Вы можете остановить его, если очистите стек в конце программы. Просто поместите это в конце:

];

Для печати вы можете использовать oNoили p(работает как `oNo)


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