Поради щодо гольфу в Джулії


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. Доступ до першого елемента масиву з[] - для масивів, вираз A[]еквівалентний A[1]. Зауважте, що це не працює для Strings, якщо ви хочете отримати перший символ або для Tuples.
  5. Не використовуйте isempty для масивів, кортежів або рядків - замість цього використовуйте ==[]для масивів та ==()кортежів; аналогічно, для негативного використання !=[]і !=(). Для рядків використовуйте ==""для порожнього, але використовуйте >""для не порожнього, оскільки "" є лексикографічно перед будь-яким іншим рядком.
  6. Використовуйте правильний булевий оператор короткого замикання замість "якщо" . Це може бути трохи менше Юлії, але це варто пам’ятати. x<=1&&"Hi"можна записати як x>1||"Hi", збереження символу (доки повернення булевого значення не важливе).
  7. Не використовуйте містить для перевірки наявності символу в рядку - якщо ви обмежені базовим ASCII, використовуйте, in('^',s)а не contains(s,"^"). Якщо ви можете використовувати інші символи, ви можете зберегти трохи більше за допомогою '^'∈s, але зауважте, що це 3 байти в UTF-8.
  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. Зворотний 1-D масив за допомогою flipud - reverse(x)на один байт довший flipud(x)і буде виконувати ту саму операцію, тому останній краще.
  12. Де можливо, використовуйте конкатенацію масиву замість push !, не змініть !, додайте! Або додайте! - для звичайних масивів це можна зробити легко. Для масивів типу 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. Не використовуйте збирання для перетворення рядка в масив 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

Спробуйте в Інтернеті!

Рекурсивні двійкові оператори

Далі показано, як перевизначити бінарний оператор для обчислення функції Ackermann:

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

але це непомірно довго. Тим НЕ менше, він робить роботу , якщо ми проходимо поплавок як лівий аргумент і ціле число в якості правого аргументу.


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-d)

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

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

  8. Використовуйте потрійний оператор Це може бути економія простору для одновиразних конструкцій If-then-Else. Застереження. Хоча це можливо і на інших мовах, ви не можете опустити порцію після двокрапки в Джулії. Також оператор є рівнем вираження в Джулії, тому ви не можете використовувати його для умовного виконання цілих блоків коду.
    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що виникає після близького батьків, тобто )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. Використовувати = замість в . [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.