CJam - это основанный на GolfScript язык стека для гольфа, созданный пользователем PPCG aditsu .
Итак, в духе других языковых советов вопросы:
Какие общие советы у вас есть для игры в гольф в CJam? Пожалуйста, оставьте один совет за ответ.
CJam - это основанный на GolfScript язык стека для гольфа, созданный пользователем PPCG aditsu .
Итак, в духе других языковых советов вопросы:
Какие общие советы у вас есть для игры в гольф в CJam? Пожалуйста, оставьте один совет за ответ.
Ответы:
Часто раздражает, что результат операции по модулю дает тот же знак, что и первый операнд. Например, -5 3%
дает -2
вместо 1
. Чаще всего ты хочешь последнее. Наивным решением является применение по модулю, добавление делителя один раз и применение по модулю снова:
3%3+3%
Но это долго и безобразно. Вместо этого, мы можем использовать тот факт , что индексация массивов всегда модульная и делает работу правильно с отрицательными индексами. Таким образом, мы просто превращаем делитель в диапазон и получаем доступ к нему:
3,=
Применительно к -5
, это дает, 1
как и ожидалось. И он всего на один байт длиннее встроенного %
!
Если модуль имеет степень 2, вы можете сохранить еще один байт, используя побитовую арихметику (что также намного быстрее). Для сравнения:
32,=
31&
Для особого случая 65536 == 2^16
другой байт может быть сохранен путем использования поведения переноса типа символа:
ci
Строка, содержащая все цифры, "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 [
.
Предположим, у вас есть целое число в стеке. Если это нечетно, вы хотите умножить его на 3 и добавить 1; в противном случае вы хотите разделить его на 2.
«Нормальный» оператор if / else будет выглядеть так:
_2%{3*)}{2/}?
Тем не менее, использование блоков, как правило, не подходит, так как {}{}
уже добавляет четыре байта. ?
также может использоваться для выбора одного из двух элементов в стеке:
_2%1$3*)@2/?
Это на один байт короче.
Блок-? с пустым оператором if всегда не допускается. Например,
{}{2/}?
на два байта длиннее
{2/}|
Если вместо этого у вас есть
{2/}{}?
и то, что вы проверяете, является неотрицательным целым числом, вы можете сделать
g)/
Новые {}&
и {}|
удобны, но иногда проблематичны, если вы не можете загромождать стек.
Тем не менее, в случае
_{…}{;}?
вместо этого вы можете использовать временную переменную:
:T{T…}&
!)/
и g)/
короче в примерах.
У 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/=~
? Мне кажется, это намного проще, и это такая же длина.
Существуют определенные короткие массивы или строки, которые время от времени возникают, например, для инициализации сеток. На самом деле, они могут стоить 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
.
Пожалуйста, не стесняйтесь расширять этот список, если вы столкнетесь с любыми другими примерами.
Если вы просто хотите очистить весь стек, оберните его в массив и вытолкните его:
];
Что еще сложнее, если вы сделали много вычислений, но хотите сохранить только верхний элемент стека и отказаться от всего, что находится ниже. Наивным подходом было бы сохранить верхний элемент в переменной, очистить стек, передать переменную. Но есть гораздо более короткая альтернатива: обернуть стек в массив и извлечь последний элемент:
]W=
(Спасибо оптимизатору, который показал мне это на днях.)
Конечно, если в стеке всего два элемента, \;
он короче.
\;
будет только выскочить элемент ниже TOS. Вы имели в виду ;;
?
e
и полномочия десятиКак и во многих других языках, вы можете писать 1e3
вместо 1000
CJam.
Это работает для нецелых базисов и даже для нецелых показателей. Например, 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].
Простой способ вычисления евклидовой нормы вектора, т. Е. Квадратного корня из суммы квадратов его элементов, имеет вид
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 за указание на это.)
Я впервые использовал этот трюк в этом ответе .
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
, если вы можете найти способ избавиться от самой значимой цифры.
$
качестве троичного, еслиКогда вы не возражаете против утечки памяти, то есть оставляя в стеке неиспользуемые элементы, которые вы позже очистите ];
, оператор копирования $
может быть удобной заменой троичного оператора ?
.
?
хорошо работает, если вам удается вычислить условие перед нажатием двух элементов на выбор, но чаще всего условие на самом деле зависит от этих элементов, и наличие его поверх них приводит к гораздо более естественным результатам.
Если у вас есть A B C
в стеке, вы можете выполнить
!$
вместо того
\@?
скопировать, B
если C
правдиво и A
иначе.
Если C
это фактическое логическое значение ( 0
или 1
), вы можете выполнить
$
вместо того
@@?
скопировать, A
если C
правдиво и B
иначе.
Допустим, у вас есть вложенный список, например матрица:
[[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
- вложенный список, который может быть полезен, если вы хотите сохранить результаты в сетке, где переменные итератора являются координатами.
Спасибо Деннису за то, что показал мне этот трюк.
f
и :
теперь были значительно улучшены, взяв любого другого оператора, включая себя. Это означает, что вы можете сэкономить еще больше байтов сейчас. Отображение оператора во вложенный список стало еще короче:
{:x}%
{x}f%
::x
Это не очень помогает с отображением блоков во вложенные списки.
Что касается применения блоков или операторов к декартовому продукту, теперь это также стало короче, как для блоков, так и для операторов:
5,_f{f{...}}
5,_ff{...}
5,_f{fx}
5,_ffx
Что приятно, теперь вы можете их вкладывать. Таким образом, вы можете легко применить оператор к третьему уровню вниз по списку:
:::x
Или блок с какой-то хитростью:
{...}ff%
f~
...
Для многих художественных задач 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 " * * * *########".
&
чтобы проверить, есть ли элемент в спискеДля
1 [1 2 3] #W>
1 [1 2 3] #)
Вы можете использовать
1 [1 2 3] &,
1 [1 2 3] &
вместо этого, который возвращает 0/1 и truey / falsey соответственно.
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
обрабатывает их как синглтоны, поэтому архивирование работает для произвольных массивов.
z
преобразование столбцов в строки, в то время как пустые значения пропускаются. В этом примере первый столбец во входных данных - 1, 2, 3, второй столбец - 4, 5 (пустая позиция пропускается), а третий столбец - 6. Затем это строки результата.
Все исключения являются фатальными в CJam. Поскольку вывод в STDERR по умолчанию игнорируется , мы можем использовать это в наших интересах.
Все операторы в CJam работают, выталкивая ноль или более элементов из стека, выполняют некоторую задачу и помещают ноль или более элементов в стек. Исключения возникают во время выполнения задачи, поэтому при этом по-прежнему всплывают элементы, но ничего не выдвигается взамен.
Вот несколько вариантов использования:
Очистка небольшого стека
Для очистки стека, который содержит два элемента, @
можно использовать. @
пытается вытолкнуть три элемента стека, но завершается неудачно после появления второго.
Любой другой оператор, который выдает три элемента, будет служить той же цели.
Смотрите это в действии здесь .
Извлечение двух или трех элементов из стека
Любой оператор, который не реализован для этих конкретных элементов, может использоваться для извлечения двух или трех элементов из стека непосредственно перед выходом.
Для выталкивания двух элементов b
работает, если один из них является символом или ни один из них не является целым числом.
Чтобы получить три элемента, t
работает, если ни один из двух нижних элементов не является итеративным, самый нижний итеративный элемент пуст или ни один из них не является целым числом.
Выход из цикла
В некоторых случаях нам нужно выйти из цикла, когда целое число становится равным нулю или строка становится слишком короткой. Вместо того, чтобы проверять эти условия, если вовлеченные операции завершаются неудачно для нуля, пустой строки или синглетонов, мы можем просто позволить программе идти своим чередом.
Для примера, включающего арифметику, см. Здесь .
Пример использования строк смотрите здесь .
Условное исполнение
Если исходный код не должен выполняться для определенных типов ввода, мы иногда можем использовать оператор, который не выполняет такой тип ввода.
Например, i
произойдет сбой для строк, которые не оцениваются как целое число, и ew
сбой для строк длиной 0 или 1.
Смотрите это в действии здесь .
Вот один для начала!
Когда вам нужно найти максимальное или минимальное число из массива, самый простой и наименьший способ - это отсортировать массив и затем извлечь первый или последний элемент.
Так что, если массив находится в переменной A
A$W=
это максимум и
A$0=
это минимум.
Получить оба одновременно можно также
A$)\0=
Это может показаться очевидным после прочтения, но первая попытка любого пользователя направлена на использование массива e<
или e>
через итерацию по массиву, который выглядит так:
A{e<}*
который на 2 байта длиннее и даже длиннее, если вы хотите и максимум, и минимум.
(
и )
вместо 0=
и W=
.
:e<
и:e>
Если вам нужно очень большое, но произвольное число, вы, как правило, будете либо использовать научную нотацию, например, 9e9
либо повышать одну из больших встроенных переменных до аналогичной степени KK#
. Однако, если вас не волнует, что такое действительное число, и оно не обязательно должно быть одинаковым (например, верхняя граница для случайного числа), вы можете сделать это в два байта, используя
es
вместо. Это дает текущую метку времени в миллисекундах и составляет порядка 10 12
e9
.
Иногда вам нужно истинное значение, когда две строки или массивы не равны, и ложное значение, если они равны. Очевидное решение - два байта:
=!
Проверьте равенство и инвертируйте результат. Однако при некоторых условиях вы можете использовать
#
Когда #
применяется к двум массивам, он фактически ищет второй массив как подмассив первого (и дает вам индекс, с которого начинается подмассив). Таким образом, если два массива одинаковы, подмассив будет найден в самом начале и даст 0
, что ложно. Но если второй массив не может быть найден, он выдаст -1
правду.
Причина, по которой нам нужны некоторые дополнительные условия для двух массивов, заключается в том, что это также дает ложное значение, если второй массив является нетривиальным префиксом первого, например:
"abc""ab"#
дает, 0
хотя строки не совпадают. Самое простое условие, которое исключает этот случай, если вы знаете, что оба массива будут одинаковой длины - в этом случае, если один является префиксом другого, вы знаете, что они равны. Но в определенных обстоятельствах могут быть более слабые условия, которые также являются достаточными. Например, если вы знаете, что строки отсортированы, префикс всегда будет первой строкой, а не второй.
c
и 16-битные целые числаЧтобы добавить (или вычесть) беззнаковые 16-битные целые числа с правильной упаковкой, вы можете использовать +65536%
или +2G#%
.
Тем не мение,
+ci
намного короче. Символы переносятся вокруг 65536 , поэтому приведение к Character ( c
), а затем к Long ( i
) имеет аналогичный эффект 65536%
с дополнительным преимуществом, что результат не будет отрицательным.
Тот же трюк можно использовать для нажатия на 65535 :
Wci
Скажем, у вас есть массив, и вы хотите массив со всеми возможными подмножествами этого массива. Хитрость заключается в том, чтобы начать с пустого массива, а затем для каждого элемента продублировать уже имеющиеся у вас подмножества и добавить в них новый элемент (сохраняя предыдущий результат, где элемент не был добавлен ). Обратите внимание, что вам нужно инициализировать стек с базовым регистром, то есть массивом, содержащим только пустой массив: это может выглядеть так:
[1 2 3 4 5]La\{1$f++}/
Хорошая вещь в этом заключается в том, что вы можете сразу выполнить некоторые вычисления для подмножества, возможно, без добавления символов. Скажем, вы хотите продукты всех подмножеств. В этом случае базовый вариант представляет собой массив, содержащий 1
, и на каждом шаге вы берете предыдущий список возможных продуктов, дублируете его и умножаете все в дубликате на новый элемент:
[1 2 3 4 5]1a\{1$f*+}/
Я думаю, что это также стоит упомянуть. Использование:
)-
Возвращает truey, если не все одинаковые, или пустой список, если все одинаковые. Ошибки, если список пуст.
В случае, если извлеченный элемент может быть сам массив (или строка):
)a-
Используйте !
или, !!
чтобы получить логические значения. В случае, если извлеченный элемент может быть массивом, и существует не более двух видов различных элементов, и вы хотите, чтобы это был 1, если не все одинаковые, это короче:
_|,(
0=
для струнныхЧтобы извлечь первый элемент массива, вы должны использовать его 0=
(или (
, если не возражаете, оставить оставшуюся часть массива в стеке).
Однако, если этот массив является строкой, приведение к символу достаточно.
"xyz"c e# Pushes 'x.
c
извлечь первый элемент любого массива, который был бы более полезным и последовательным.
В 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].
Пока у вас нет открытия [
, этот трюк также можно использовать для поворота всего стека, т. Е. Для перемещения самого нижнего элемента стека наверх:
]:\
Допустим, в вашем стеке есть список строк / чисел / и т. Д. сверху и некоторые другие дополнительные предметы под ним. т.е.
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"]
который является точным представлением списка, когда он был в стеке!
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*{(+_&}%}*
Это все, ребята. на данный момент .
]:m*:e_
с любым количеством массивов
Иногда, если вы работаете со сложной структурой данных, в то время как элементы в ней просты, преобразование в строки может помочь.
Например, если вы хотите получить первые или последние несколько элементов в двумерном массиве битов и не заботитесь о возвращаемом типе, sA<
сохраните байт из 0=A<
или :+A<
.
Или, если вы хотите изменить некоторые биты на входе, вы можете изменить строку перед ее оценкой.
Или, если вы получили эту структуру и хотите преобразовать ее в простой список:
[[[[[[[[[1]2]3]4]5]6]7]8]9]
Вы можете сделать это со многими персонажами другими способами:
[a{~)\}h;]W%
Но это может быть намного короче со строками:
s:~
Он короче, даже если в нем могут быть цифры, состоящие из более чем одной цифры:
[`La`-~]
Или:
`']-~]
Если вам не нужен другой массив, содержащий много таких массивов.
e_
сейчас
s
все еще работает лучше.
N
вместоLa
Во многих случаях вам нужно что-то инициализировать для массива, содержащего пустой массив в качестве его единственного элемента, который La
, казалось бы, на 1 байт длиннее.
Во многих случаях вам также необходимо добавить новую строку после каждого элемента перед печатью, что будет что-то вроде No
или N*
.
Но если оба значения верны, иногда вы можете обнаружить, что можете просто инициализировать массив с N
помощью символа новой строки в качестве единственного элемента. Удостоверьтесь, что вы добавляете вещи только к элементам в остальном коде, и первым, что нужно добавить, всегда является символ или массив. Или только добавить, если лидирующий символ новой строки приемлем и это делает его короче.
Иногда S
также работает, если вам нужно разделить вывод пробелами.
В более редких случаях начальный элемент должен быть строкой. Но вы все еще можете использовать, Na
который может быть короче, чем добавление новой строки после этого.
Скажем, у вас есть строка, "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 за то, что указал на это в одном из моих ответов сегодня.
Мы имеем :x
в качестве сокращения для {x}%
и или {x}*
(в зависимости от того, x
является ли унарный или двоичный). К сожалению, нет эквивалентного инфиксного оператора для сокращения {x}/
. Однако, очень часто, когда мы это делаем {x}/
, x
на самом деле это бинарный оператор, который многократно изменяет элемент, лежащий внизу в стеке. Если это так, и указанный элемент не является массивом, мы можем сохранить байт, использовав сложение / уменьшение как foreach:
5 [1 2 3 4]{-}/ e# Gives -5
5 [1 2 3 4]+:-
Это работает, потому что сгиб всегда оставляет первый элемент без изменений. К сожалению, он не сохраняет байт, когда модифицированный элемент является массивом, потому что его добавление развернет его. Однако, иногда вам повезло, что ваш массив уже содержит этот элемент спереди, и в этом случае следует помнить о сокращении (вместо того, чтобы вручную удалять элемент перед использованием {}/
в оставшейся части).
CJam имеет print
оператор: o
. Это работает, но стек печатает сразу после выполнения всего кода. Вы можете остановить его, если очистите стек в конце программы. Просто поместите это в конце:
];
Для печати вы можете использовать oNo
или p
(работает как `oNo
)
o
и p
. p
начинается с преобразования элемента для печати в однозначное строковое представление. p
эквивалентно выполнению `oNo
.