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


20

Какие общие советы у вас есть для игры в гольф в Юлии? Я ищу идеи, которые могут быть применены к кодовым проблемам гольфа в целом, которые, по крайней мере, несколько специфичны для Джулии (например, «удалить комментарии» - это не ответ).

Ответы:


19

ПРИМЕЧАНИЕ: ниже могут содержаться некоторые устаревшие советы, так как Юлия еще не совсем стабилизировалась с точки зрения структуры.

Несколько трюков, чтобы сохранить несколько персонажей

  1. Операторы перегрузки с часто используемыми двоичными функциями . Например, если вам нужно сделать много целочисленных делений и нет необходимости в обратном делении, используйте \ =div, и тогда вы сможете печатать a\bвместо div(a,b). Обратите внимание на пробел - это необходимо, чтобы избежать его синтаксического анализа как оператора "\ =". Также обратите внимание, что, если перегружено на уровне подсказки REPL, используйте (\)=Base.(\)или \ =Base. \для его сброса. ПРИМЕЧАНИЕ: некоторые функции имеют предопределенные существующие операторы UTF-8, например, ÷для div, как отметил Алекс А.
  2. Используйте ^ со строками для условного вывода . То есть вместо того a>0?"Hi":"", "Hi"^(a>0)чтобы использовать для сохранения одного байта, или для логического a, используйте "Hi"^aдля сохранения трех байтов.
  3. (иногда) Храните маленькие векторы фиксированного размера как отдельные переменные . Например, вместо a=split("Hi there"," "), вы можете избежать a[1]и a[2]использовать a,b=split("Hi there"," "), на который можно ссылаться как aи b, сохраняя три байта для каждого использования, за счет всего двух дополнительных символов при назначении. Очевидно, не делайте этого, если вы можете работать с векторными операциями.
  4. Получите доступ к первому элементу Array с помощью[] - для массивов, выражение A[]эквивалентно A[1]. Обратите внимание, что это не работает для строк, если вы хотите получить первый символ, или для кортежей.
  5. Не используйте isempty для массивов, кортежей или строк - вместо этого используйте ==[]для массивов и ==()кортежей; аналогично для негатива используйте !=[]и !=(). Для строк, используйте ==""для пустого, но используйте >""для не пустого, так как "" лексикографически перед любой другой строкой.
  6. Используйте правильный логический оператор короткого замыкания вместо «если» . Это может быть немного менее специфично для Юлии, но это стоит иметь в виду. x<=1&&"Hi"можно записать как x>1||"Hi"сохранение символа (если возвращение логического значения не имеет значения).
  7. Не используйте содержащийся для проверки наличия символа в строке - если вы ограничены базовым ASCII, используйте in('^',s)вместо contains(s,"^"). Если вы можете использовать другие символы, вы можете сэкономить немного больше '^'∈s, но учтите, что в UTF-8 это 3 байта.
  8. Ищете минимальные / максимальные значения в массиве? Не используйте минимум или максимум - вместо того, чтобы использовать minimum(x)или maximum(x), используйте min(x...)или max(x...), чтобы убрать один символ из вашего кода, если вы знаете, xбудет иметь по крайней мере два элемента. В качестве альтернативы, если вы знаете, что все элементы xбудут неотрицательными, используйте minabs(x)илиmaxabs(x)
  9. Там, где это возможно и разрешено заданием, используйте вместо \ n фактический символ новой строки - обратите внимание, что это сделает ваш код труднее для чтения, и это может означать, что вам нужно предоставить версию «без заглядывания», чтобы люди могли на самом деле понять Это.
  10. Поставьте опции после строки регулярного выражения - если вы хотите иметь строку регулярного выражения в многострочном режиме, например, не печатайте r"(?m)match^ this", печатайте r"match^ this"m, сохраняя три символа.
  11. Обратные одномерные массивы с использованием flipud - reverse(x)на один байт длиннее flipud(x)и будут выполнять ту же операцию, поэтому последний лучше.
  12. Где возможно, используйте конкатенацию массива вместо push !, unshift !, append !, или prepend! - для обычных массивов это можно сделать легко. Для массивов типа Any с элементами массива вам понадобятся фигурные скобки вокруг добавленных массивов (то есть {[1,2]}, нет {1,2}) - для Julia 0.4 вам понадобится Any[[1,2]].
  13. Используйте индексирование массива, чтобы получить размер массива или строки - когда вы используете endиндексирование массива, оно автоматически преобразуется в длину массива / строки. Так что вместо k=length(A), используйте, A[k=end]чтобы сохранить 3 символа. Обратите внимание, что это может быть не выгодно, если вы хотите использовать k немедленно. Это также работает в многомерном случае - A[k=end,l=end]получит размер каждого измерения A- однако (k,l)=size(A)в этом случае он будет короче на один байт, поэтому используйте его, только если вы хотите сразу получить доступ к последнему элементу одновременно.
  14. Получить итератор индекса с помощью индексации массива - Подобно 13, вы также можете получить итератор, соответствующий длине массива A[k=1:end], в этом случае kбудет выполняться сопоставление итератора 1:length(A). Это может быть полезно, когда вы хотите использовать массив Aодновременно.
  15. Не используйте метод collect для преобразования строки в массив char - вместо этого collect(A)используйте [A...]метод, который сделает то же самое и сэкономит 4 байта.
  16. Нужно преобразовать число в строку? Используйте "$(s[i])"или dec(s[i])для выражений или многосимвольных переменных, и "$i"для односимвольных переменных.
  17. Используйте ?:вместо &&или ||для условного присваивания - то есть, если вы хотите выполнить присваивание только при некотором условии, вы можете сохранить один байт, записав cond?A=B:1вместо cond&&(A=B), или cond?1:A=Bвместо cond||(A=B). Обратите внимание, что 1здесь это фиктивное значение.
  18. Используйте unionили вместоunique - union(s)сделайте то же самое unique(s), что и сохраните байт в процессе. Если вы можете использовать не-ASCII символы, то ∪(s)будете делать то же самое, и стоит всего 3 байта вместо 5 байтов в union.

2
О, как бы мне понравился этот первый трюк в Python.
seequ

Вы можете разделить пробелы, используя просто, split("Hi there")поскольку аргумент шаблона по умолчанию равен пробелу.
Алекс А.

@AlexA. - Я знаю, но дело не в наконечнике, и наконечник применяется одинаково хорошо в любом случае.
Глен О

Пункт 12 изменился в 0,5.
Линдон Уайт

@Oxinabox - Я не удивлен, я уверен, что некоторые из них уже устарели. Я первоначально написал большинство советов для 0.3, я думаю.
Глен О

15

Переопределить операторы для определения функций

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

Рекурсивные унарные операторы

Для унарного примера сравните следующие рекурсивные реализации последовательности Фибоначчи:

F(n)=n>1?F(n-1)+F(n-2):n # 24 bytes
!n=n>1?!~-n+!(n-2):n     # 20 bytes
!n=n>1?!~-n+!~-~-n:n     # 20 bytes

Попробуйте онлайн!

Переопределенный оператор сохраняет свой первоначальный приоритет.

Обратите внимание, что мы не могли просто поменяться местами !в пользу ~, поскольку ~он уже определен для целых чисел, а !определен только для логических значений.

Бинарные операторы

Даже без рекурсии переопределение оператора короче, чем определение бинарной функции. Сравните следующие определения простого теста делимости.

f(x,y)=x==0?y==0:y%x==0 # 23 bytes
(x,y)->x==0?y==0:y%x==0 # 23 bytes
x->y->x==0?y==0:y%x==0  # 22 bytes
x\y=x==0?y==0:y%x==0    # 20 bytes

Попробуйте онлайн!

Рекурсивные бинарные операторы

Ниже показано, как переопределить бинарный оператор для вычисления функции Аккермана:

A(m,n)=m>0?A(m-1,n<1||A(m,n-1)):n+1    # 35 bytes
^ =(m,n)->m>0?(m-1)^(n<1||m^~-n):n+1   # 36 bytes
| =(m,n)->m>0?m-1|(n<1||m|~-n):n+1     # 34 bytes
m\n=m>0?~-m\(n<1||m\~-n):n+1           # 28 bytes

Попробуйте онлайн!

Обратите внимание, что ^это даже дольше, чем использование обычного идентификатора, поскольку его приоритет слишком высок.

Как упоминалось ранее

m|n=m>0?m-1|(n<1||m|~-n):n+1           # 28 bytes

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

m::Int|n::Int=m>0?m-1|(n<1||m|~-n):n+1 # 38 bytes

но это непомерно долго. Тем не менее, это работает, если мы передаем float в качестве левого аргумента и целое число в качестве правого аргумента.


11
  1. Не будьте слишком легко соблазнены фактором (n) У заманчивой функции базовой библиотеки factor(n)есть фатальный недостаток: она возвращает факторизацию вашего целого числа в неупорядоченном Dictтипе. Таким образом, для получения данных, которые вы хотели получить , требуются дорогостоящие collect(keys())и, collect(values())потенциально, также catи sort. Во многих случаях может быть дешевле просто учесть фактическое деление. Грустно, но верно.

  2. Использование карты map - отличная альтернатива зацикливанию. Помните о разнице между mapи map!и используйте функциональные возможности последних, когда это возможно.

  3. Использование mapreduce mapreduce расширяет функциональные возможности карты и может значительно сэкономить на байтах.

  4. Анонимные функции великолепны! .. особенно, когда перешли к вышеупомянутым mapфункциям.

  5. Функции кумулятивного массива cumprod , cumsumароматизатор cumminи другие функции с аналогичными именами обеспечивают кумулятивные операции в указанном измерении n-мерного массива. (Или * un * указано, если массив 1-й)

  6. Краткая запись для Any Если вы хотите выбрать все конкретные измерения многомерного массива (или Dict), например A[Any,2], вы можете сохранить байты с помощьюA[:,2]

  7. Используйте однострочное обозначение для функций. Вместо того, function f(x) begin ... endчтобы часто упрощатьf(x)=(...)

  8. Используйте троичный оператор. Это может быть экономия пространства для конструкций If-Then-Else с одним выражением. Предостережения: Хотя это возможно на некоторых других языках, вы не можете опустить часть после двоеточия в Юлии. Кроме того, в Julia оператор является уровнем выражения, поэтому его нельзя использовать для условного выполнения целых блоков кода.
    if x<10 then true else false endпротив
    x<10?true:false


3
Как на земле «использовать троичный оператор» несколько специфично для Юлии? Это относится к каждому языку, который имеет его.
Питер Тейлор

5
Уместно, что оно есть. Во многих языках также есть карта, анонимные или чистые функции, своего рода сокращение для любых / всех, кумулятивные функции и т. Д. Если бы мы сводили каждую ветку подсказок только к функциям, абсолютно уникальным для этого языка, было бы очень мало содержания подсказок. ,
Джонатан Ван Матре

3
Черт возьми, только все функциональные для начинающих, где все возвращает значение, поэтому троичная операция будет избыточной. Если вам нужны конкретные примеры: Go, Haskell, Scala, Lua, VB, Prolog, PL / SQL ... даже Python не для многих версий. Из почти дюжины языков, в чьих ветках подсказок упоминается их троичный оператор, есть ли какая-то причина, по которой вы решили стать провинциальным в моем?
Джонатан Ван Матре

3
Ну, эй, по крайней мере, ты равнодушный. ヘ ( ̄ ー  ̄ ヘ)
Джонатан Ван Матре

1
Могу ли я предложить изменить совет 3? mapreduce длиннее, чем mapfoldl или mapfoldr, и может иметь различное поведение в зависимости от реализации. mapfoldl и mapfoldr последовательно ассоциируются слева и справа (соответственно) и, таким образом, являются лучшим выбором. Это также относится в целом к ​​уменьшению (используйте foldl или foldr).
Глен О

9

Перебирать функции

Это также возможно в других языках, но обычно дольше, чем простой метод. Тем не менее, способность Джулии переопределить свои унарные и бинарные операторы делает ее довольно гольфы.

Например, чтобы создать таблицу сложения, вычитания, умножения и деления для натуральных чисел от 1 до 10, можно использовать

[x|y for x=1:10,y=1:10,| =(+,-,*,÷)]

который переопределяет бинарный оператор , |как +, -, *и ÷, затем вычисляет x|yдля каждой операции и xи yв желаемых пределах.

Это работает и для унарных операторов. Например, для вычисления комплексных чисел 1 + 2i , 3-4i , -5 + 6i и -7-8i , их негативов, их комплексных конъюгатов и мультипликативных инверсий можно использовать

[~x for~=(+,-,conj,inv),x=(1+2im,3-4im,-5+6im,-7-8im)]

который переопределяет унарную оператор , ~как +, -, conjи inv, затем вычисляет ~xдля всех желаемых комплексных чисел.

Примеры в реальных конкурсах


6
  1. Ключевые слова иногда могут сразу следовать за константами без необходимости использовать пробел или точку с запятой. Например:

    n->(for i=1:n n-=1end;n)

    Обратите внимание на отсутствие пробела между 1и end. Это также верно для endпроисходящих после близкого Paren, то есть )end.

  2. Выполните целочисленное деление , используя ÷вместо того , div()или перегрузки оператора. Обратите внимание, что ÷в UTF-8 стоит 2 байта.

  3. Используйте vec()или A[:](для некоторого массива A), а не reshape()когда это возможно.

  4. Создавайте функции, а не полные программы, если это разрешено правилами соревнований Короче определить функцию, которая принимает ввод, а не определять переменные, читая из stdin. Например:

    n->(n^2-1)
    n=read(STDIN,Int);n^2-1
  5. Переменные могут быть увеличены внутри аргумента функции. Например, ниже приведен мой ответ на задачу « Найти следующий 1-разреженный двоичный номер» :

    n->(while contains(bin(n+=1),"11")end;n)

    Это короче, чем увеличение nвнутри цикла.


6
  1. Не типreturn f(x)=x+4 идентичен, но короче f(x)=return x+4. Юлия всегда возвращает результат последнего утверждения.
  2. Используйте = вместо in . [x for x in 1:4]на 3 символа длиннее, но эквивалентно[x for x=1:4]

5

Используйте функцию трансляции вызовов.

Введено в Юлия 0,5. Это похоже на карту, но использует меньше символов и транслирует поведение по своим аргументам - это означает, что вы можете писать меньше лямбда-выражений, чтобы иметь дело с вещами.

Скорее, чем:

  • map(f,x) - 8 символов.
  • f.(x) - 5 символов

Еще лучше:

  • map(a->g(a,y),x) - 16 символов
  • g.(x,[y]) - 9 символов
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.