Які загальні поради щодо гольфу в Джулії? Я шукаю ідеї, які можна застосувати до проблем із гольфом взагалі, які принаймні дещо характерні для Юлії (наприклад, "видалити коментарі" - це не відповідь).
Які загальні поради щодо гольфу в Джулії? Я шукаю ідеї, які можна застосувати до проблем із гольфом взагалі, які принаймні дещо характерні для Юлії (наприклад, "видалити коментарі" - це не відповідь).
Відповіді:
ПРИМІТКА. Нижче наведено деякі застарілі поради, оскільки Юлія ще не досить стабілізована за структурою.
Кілька хитрощів, щоб зберегти кілька персонажів
\ =div
, а потім можете вводити a\b
замість div(a,b)
. Зверніть увагу на пробіл - це необхідно, щоб уникнути його розбору як "\ =" оператора. Також зауважте, що при перевантаженні на рівні запиту REPL використовуйте (\)=Base.(\)
або \ =Base. \
скиньте його. Примітка: деякі функції вже є UTF-8 оператори заздалегідь визначені, наприклад, ÷
для div
, як зазначає Алекс А.a>0?"Hi":""
використовувати "Hi"^(a>0)
для збереження одного байта, або для булевого a, використовувати "Hi"^a
для збереження трьох байтів.a=split("Hi there"," ")
, ви можете уникнути a[1]
та a[2]
використовувати a,b=split("Hi there"," ")
, на що можна посилатися як a
і b
, економлячи три байти на кожне використання, ціною лише двох зайвих символів при призначенні. Очевидно, не робіть цього, якщо ви можете працювати з векторними операціями.[]
- для масивів, вираз A[]
еквівалентний A[1]
. Зауважте, що це не працює для Strings, якщо ви хочете отримати перший символ або для Tuples.==[]
для масивів та ==()
кортежів; аналогічно, для негативного використання !=[]
і !=()
. Для рядків використовуйте ==""
для порожнього, але використовуйте >""
для не порожнього, оскільки "" є лексикографічно перед будь-яким іншим рядком.x<=1&&"Hi"
можна записати як x>1||"Hi"
, збереження символу (доки повернення булевого значення не важливе).in('^',s)
а не contains(s,"^")
. Якщо ви можете використовувати інші символи, ви можете зберегти трохи більше за допомогою '^'∈s
, але зауважте, що ∈
це 3 байти в UTF-8.minimum(x)
або maximum(x)
використовуйте min(x...)
або max(x...)
, щоб голити один символ зі свого коду, якщо ви знаєте, що x
буде мати принаймні два елементи. Крім того, якщо ви знаєте, що всі елементи x
не будуть негативними, використовуйте minabs(x)
абоmaxabs(x)
r"(?m)match^ this"
, вводити r"match^ this"m
, зберігаючи три символи.reverse(x)
на один байт довший flipud(x)
і буде виконувати ту саму операцію, тому останній краще.{[1,2]}
, ні {1,2}
) - для Julia 0.4 вам знадобиться Any[[1,2]]
.end
в межах індексації масиву, він автоматично перетворюється на довжину масиву / рядка. Тому замість цього k=length(A)
використовуйте A[k=end]
для збереження 3 символів. Зауважте, що це може бути не корисно, якщо ви хочете скористатися k негайно. Це також працює в багатовимірному випадку - A[k=end,l=end]
отримає розмір кожного виміру A
- однак (k,l)=size(A)
у цьому випадку він коротший на один байт, тому використовуйте його лише в тому випадку, якщо ви хочете одразу отримати доступ до останнього елемента.A[k=1:end]
, і в цьому випадку k
буде проведено збіг ітератора 1:length(A)
. Це може бути корисно, коли ви також хочете одночасно використовувати масив A
.collect(A)
, щоб використовувати [A...]
, що зробить те ж саме і збереже 4 байти."$(s[i])"
або dec(s[i])
для виразів, або для багатозначних змінних, і "$i"
для однозначних змінних.?:
замість &&
або ||
для умовного призначення - тобто, якщо ви хочете виконати завдання лише за певної умови, ви можете зберегти один байт, записавши, cond?A=B:1
а не cond&&(A=B)
, cond?1:A=B
а не cond||(A=B)
. Зауважте, що 1
тут - манекенне значення.union
або ∪
замістьunique
- union(s)
зробить те саме unique(s)
, що і збереже байт у процесі. Якщо ви можете використовувати символи, що не належать до ASCII, тоді ∪(s)
ви зробите те саме, і ∪
коштуєте лише 3 байти замість 5 байт в union
.split("Hi there")
оскільки аргумент шаблона за замовчуванням пробіл.
Повторне визначення операторів може зберегти багато байтів у дужках та комах.
Для одинарного прикладу порівняйте такі рекурсивні реалізації послідовності Фібоначчі:
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
але це непомірно довго. Тим НЕ менше, він робить роботу , якщо ми проходимо поплавок як лівий аргумент і ціле число в якості правого аргументу.
Не будьте занадто легко спокушені фактором (n) Приваблива функція базової бібліотеки factor(n)
має фатальний недолік: вона повертає факторизацію вашого цілого числа у невпорядкованому Dict
вигляді. Таким чином, для отримання потрібних даних потрібне дорого, collect(keys())
а collect(values())
також, можливо, також cat
і а sort
. У багатьох випадках може бути дешевше просто врахувати фактор шляхом пробного поділу. Сумно але правда.
Карта використання map
- відмінна альтернатива циклічному циклу. Будьте в курсі різниці між map
та map!
використовуйте функціонал останніх, коли зможете.
Використання mapreduce mapreduce
розширює функціональність карти ще більше і може бути значним збереженням байтів.
Анонімні функції чудові! .. особливо при переході до вищезазначених map
функцій.
Функції сукупного масиву cumprod
, cumsum
смакова cummin
та інші аналогічно названі функції дозволяють здійснювати кумулятивні операції вздовж визначеного розміру n-мірного масиву. (Або * un * вказано, якщо масив 1-d)
Коротке позначення для будь-якого, коли потрібно вибрати всі конкретні розміри багатовимірного масиву (або Dict), наприклад A[Any,2]
, ви можете зберегти байти за допомогоюA[:,2]
Використовуйте однорядкові позначення для функцій Замість цього function f(x) begin ... end
ви можете часто спрощуватиf(x)=(...)
Використовуйте потрійний оператор Це може бути економія простору для одновиразних конструкцій If-then-Else. Застереження. Хоча це можливо і на інших мовах, ви не можете опустити порцію після двокрапки в Джулії. Також оператор є рівнем вираження в Джулії, тому ви не можете використовувати його для умовного виконання цілих блоків коду.
if x<10 then true else false end
проти
x<10?true:false
Це можливо і в інших мовах, але зазвичай довше, ніж прямолінійний метод. Однак здатність Джулії переосмислити свої одинарні та бінарні оператори роблять її досить голодною.
Наприклад, для створення таблиці додавання, віднімання, множення та ділення для натуральних чисел від 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
для всіх бажаних комплексних чисел.
Жіноча та чоловіча послідовності (двійкові)
Перестановка випадку (одинарна)
Ключові слова іноді можуть відразу слідувати за константами, не потребуючи пробілу чи крапки з комою. Наприклад:
n->(for i=1:n n-=1end;n)
Зверніть увагу на відсутність пробілу між 1
та end
. Це справедливо і для того, end
що виникає після близького батьків, тобто )end
.
Виконайте ціле ділення, використовуючи оператор, ÷
а не div()
перевантажуючи його чи не перевантажуючи його. Зауважте, що ÷
в UTF-8 варто 2 байти.
Використовуйте vec()
або A[:]
(для деякого масиву A
), а не reshape()
коли це можливо.
Створюйте функції, а не повноцінні програми, якщо це дозволено в правилах виклику. Коротше визначити функцію, яка приймає введення, а не визначення змінних, читаючи з stdin. Наприклад:
n->(n^2-1)
n=read(STDIN,Int);n^2-1
Змінні можуть бути збільшені всередині аргументу до функції. Наприклад, наступна моя відповідь на завдання « Знайти наступне 1-розрядне бінарне число» :
n->(while contains(bin(n+=1),"11")end;n)
Це коротше, ніж приріст n
всередині петлі.
Введено в Джулії 0,5. Це як карта, але використовує менше символів, і мовлення поводиться над своїми аргами, а це означає, що ви можете писати менше лямбда для вирішення речей.
Замість:
map(f,x)
- 8 символів.f.(x)
- 5 символівЩе краще:
map(a->g(a,y),x)
- 16 символівg.(x,[y])
- 9 символів