Відповіді:
Підказки нижче відрізняються від найекономічніших до найчастіше використовуваних:
Використовуйте команди високого рівня Mathematica, де це можливо, навіть об'ємні:
MorphologicalComponents
: Код-Гольф: Острови графів
Можливості маніпулювання зображеннями: наприклад, сьогодні (24 вересня) день народження HONDA
Subsets
IntegerPartitions
Заходи відстані та подібності: наприклад, EuclideanDistance
може бути збереження байту. Зауважте, що писати Total@Abs[a-b]
замість a~ManhattanDistance~b
і Max@Abs[a-b]
замість цього зазвичай коротше a~ChessboardDistance~b
.
Використання Graphics and
Text
для мистецтва Ascii: наприклад, програмування зірок!
і побудувати аналоговий годинник
Виділені символи:
логіка та встановлення символів операцій замість їх довгих форм імен: ⋂, ⋃, ∧, ∨
Map
і Apply
: /@
, //@
. @@
,@@@
Позначення префікса та інфіксації:
Print@"hello"
замість Print["hello"]
a~f~b
замість f[a,b]
Якщо функція використовується лише один раз, чиста функція може економити символ або два.
Приєднання рядків до списку. ""<>{"a","b","c"}
замістьStringJoin@{"a","b","c"}
Використовуйте функції, що можна відстежувати. Чим довші списки, тим краще.
{a, b, c} + {x, y, z}= {a+x, b+y, c+z}
{2, 3, 4} {5, 6, 7}= {10, 18, 28}
{{a, b}, {c, d}}^{2, 3} = {{a^2, b^2}, {c^3, d^3}}
Деякі вбудовані функції з довгими іменами можна замінити коротшими виразами.
Наприклад:
Total
=> Tr
Transpose
=> Thread
або\[Transpose]
True
=> 1<2
False
=> 1>2
Times
=> 1##&
Alternatives
=> $|##&
IntegerQ
=> ⌊#⌋==#&
a[[1]]
=> #&@@a
a[[All,1]]
=> #&@@@a
ConstantArray[a,n]
=> Array[a&,n]
абоTable[a,{n}]
Union@a
=> {}⋃a
абоa⋃a
ToExpression@n
=> FromDigits@n
якщо n
це числоDivisible[n,m]
=> m∣n
FromDigits[n,2]
=> Fold[#+##&,n]
якщо n
є список 0
s і 1
sComplex@z
=> {1,I}.z
де z
перелік форми{x,y}
Thread[{{a,b},{c,d}}]
== Thread[List[{a,b},{c,d}]]
== {List[a,c],List[b,d]}
== {{a,c},{b,d}}
==Transpose[{{a,b},{c,d}}]
Fold
трюк FromDigits
також працює для будь-якої іншої бази, крім 10
. Наприклад FromDigits[n,5]
-> Fold[4#+##&,n]
(з бонусом збереження додаткового байта для баз 100
і 1000
).
U+F3C7
.
Echo
, що це варіант, оскільки він друкує >>
(і пробіл) на STDOUT перед друком фактичного рядка.
Complex[x,y] => {1,I}.{x,y}
я думаю x+y*I
, що набагато коротше з тим же ефектом?
Це досить поширений вектор для роботи з:
{0,0}
Виявляється, це можна скоротити на байт:
0{,}
Ще більше байтів зберігається, якщо вектор довший двох нулів. Це також можна використовувати для ініціалізації нульових матриць, наприклад, наступна дає матрицю 2х2 нулів:
0{{,},{,}}
Це також можна використовувати для ненульових значень, якщо вони достатньо великі або достатньо багато або негативні. Порівняйте такі пари:
{100,100}
0{,}+100
{-1,-1}
0{,}-1
{3,3,3,3}
0{,,,}+3
Але пам’ятайте, що починаючи з 6 значень, вам 1~Table~6
в цьому випадку краще (можливо, раніше), залежно від вимог пріоритетності).
Причина цього працює в тому, що ,
вводиться два аргументи до списку, але опущені аргументи (будь-де в Mathematica) є неявними Null
s. Крім того, множення Listable
, і 0*x
є 0
практично для будь-якого x
(за винятком речей , як Infinity
і Indeterminate
), так ось що відбувається:
0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}
Для списків 1
s можна скористатися аналогічним трюком, використовуючи правила експоненції. Існує два різні способи збереження байтів, якщо 1
у списку є принаймні три s:
{1,1,1}
1^{,,}
{,,}^0
1^{,,,}
на один байт менше, ніж 0{,,,}+1
.
{,,}^0
. Я відредагую публікацію.
Під час коду для гольфу ви часто використовуєте функціональний підхід, коли ви використовуєте анонімні (чисті) функції зі &
скороченим синтаксисом. Існує дуже багато різних способів отримати доступ до аргументів такої функції, і ви часто можете поголити пару байтів, добре зрозумівши свої можливості.
Ви, мабуть, знаєте це, якщо раніше ви використовували чисті функції. П - й аргумент називається #n
, і #
діє в якості псевдоніма для #1
. Отже, якщо, скажімо, ви хочете написати функцію, яка приймає в якості параметрів іншу функцію та її аргумент (щоб передати аргумент цій функції), використовуйте
#@#2&
Це не працює з негативними номерами (наприклад, ви можете використовуватись для доступу до списків).
Однією з головних нових мовних особливостей у Mathematica 10 є Association
s, які в основному є ключовими картами з довільними типами ключів, написаними як
<| x -> 1, "abc" -> 2, 5 -> 3 |>
Якщо така асоціація передається як перший аргумент чистої функції, ви можете отримати доступ до деяких, якщо її аргументи як названі параметри:
{#, #2, #3, #abc, #xyz} & [<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th"]
(* {<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th", "1st", "2nd"} *)
Зауважте, що #
як і раніше відноситься до всієї асоціації. Щоб названі параметри працювали, ключами повинні бути рядки (це не буде працювати, якщо ви використовуєте, наприклад, невизначені змінні), і ці рядки повинні починатися з літери і містити лише літери та цифри.
#0
Менш відома функція - це те, що #0
також існує і дає вам сам об’єкт функції. Це може бути дуже корисно в лайках і узагальнених лайках. Насправді найкоротша квітка Mathematica (я знаю)
ToString[#0][] & []
Що трохи дратує, що це не дасть точних символів, які ви ввели. Наприклад, якщо використовується @
для додатка функції, він все одно буде відображатися, оскільки [...]
місцями будуть вставлені пробіли. Зазвичай це зробить лайки трохи довше, ніж ви хотіли б бути, але це завжди спрацює, спочатку гольфувавши по квилці, а потім просто скопіювавши її вихід - що тепер має бути справжньою королевою.
Крім quines, це також означає, що ви можете писати рекурсивний код, не називаючи своєї функції. Порівняйте ці три (наївні, але гольф) реалізації Фібоначчі:
f@0=0;f@1=1;f@n_:=f[n-1]+f[n-2]
f@n_:=If[n<2,n,f[n-1]+f[n-2]]
If[#<2,#,#0[#-1]+#0[#-2]]&
Тепер саме тут починається справжня магія. Послідовності не використовуються часто в гольфі, тому що Sequence
це занадто довге ім'я, щоб воно було вартим більшу частину часу. Але в чистих функціях - це те, де вони світяться. Якщо ви не знайомі з послідовностями, вони, як правило, подібні до символів на деяких інших мовах, якщо ви використовуєте послідовність у списку List
або аргументі функції, елементи автоматично розкладаються на окремі слоти. Так
{1, Sequence[2, 3, 4], 5} == {1, 2, 3, 4, 5}
f["a", Sequence[0, {}], "b"] == f["a", 0, {}, "b"]
Тепер у чистих функціях ##
або ##1
це послідовність усіх аргументів. Крім того, ##2
послідовність всіх аргументів , починаючи з другого, ##3
всі аргументи , починаючи з третього і т.д. Таким чином , для початку, ми можемо просто перевизначити , Sequence
як ##&
, заощадивши 5 байт. Як приклад використання, це дає нам альтернативу Join@@list
(див. Цю пораду ), яка не економить жодних байтів, але добре все-таки знати про це:
##&@@@list
Це ефективно розгладжує перший рівень вкладеного списку. Що ще ми можемо з цим зробити? Ось на 2 байти коротша альтернатива RotateLeft
:
RotateLeft@list
{##2,#}&@list
Тільки для цих речей варто пам’ятати про цю особливість. Однак ми можемо зробити краще! Послідовності стають дійсно цікавими, якщо врахувати, що оператори реально реалізовані як функції під кришкою. Наприклад, a+b
фактично оцінюється до Plus[a,b]
. Тож якщо ми дамо цю послідовність ...
1+##&[1,2,3]
=> Plus[1,##]
=> Plus[1,1,2,3]
=> 7
Ця хитрість була використана в цій підказці, щоб зберегти байт Times
, тому що супозиція технічно також є лише оператором:
1##&[1,2,3]
=> Times[1,##]
=> Times[1,1,2,3]
=> 6
Ви також можете використовувати його для збереження байта, Unequal
якщо у вас є однозначне значення або змінна, яку ви знаєте, немає у ваших аргументах ( N
можливо, це буде працювати в 99% випадків):
Unequal[a,b,c]
N!=##&[a,b,c]
Це стає ще більш цікавим з унарні і -
та /
- останні два фактично реалізовані в термінах множення і зведення в ступінь. Ось список дій, які ви можете зробити, де останній стовпець передбачає, що функція передала аргументи a, b, c
:
Operator Function Expanded Equivalent to
+## Plus[##] Plus[a,b,c] a+b+c
1## Times[1,##] Times[1,a,b,c] a*b*c
-## Times[-1,##] Times[-1,a,b,c] -a*b*c
x+## Plus[x,##] Plus[x,a,b,c] x+a+b+c
x-## Plus[x,Times[-1,##]] Plus[x,Times[-1,a,b,c]] x-a*b*c
x## Times[x,##] Times[x,a,b,c] x*a*b*c
x/## Times[x,Power[##,-1]] Times[x,Power[a,b,c,-1]] x*a^b^c^-1
##/x Times[##,Power[x,-1]] Times[a,b,c,Power[x,-1]] a*b*c/x
x^## Power[x,##] Power[x,a,b,c] x^a^b^c
##^x Power[##,x] Power[a,b,c,#] a^b^c^x
x.## Dot[x,##] Dot[x,a,b,c] x.a.b.c
Інші загальні оператори !=
, ==
, &&
, ||
. Менш поширені з них , щоб мати на увазі , є |
, @*
, /*
. На закінчення, ось невеликий бонусний трюк:
#### Times[##,##] Times[a,b,c,a,b,c] (a*b*c)^2
Продовжуйте експериментувати з цим, і повідомте мені, чи знайдете ви якісь інші корисні або особливо цікаві програми!
Sqrt@2
або 2^.5
=>√2
a[[1]]
=>a〚1〛
#+#2&
=>+##&
Flatten@a
=> Join@@a
(іноді)
Function[x,x^2]
=> xx^2
або#^2&
a〚1;;-1;;2〛
=>a〚;;;;2〛
a〚2;;-1 ;;2〛
=>a〚2;;;;2〛
a〚All,1〛
=>a〚;;,1〛
{{1}}〚1,1〛
=>Tr@{{1}}
0&~Array~10
=>0Range@10
Range[10^3]
=>Range@1*^3
〚
і 〛
бере 3 байти кожен (припустимо, UTF8)
Надихнувшись нещодавним відкриттям Денніса для Джулії, я подумав, що я буду розбиратися в цьому для Mathematica. Я усвідомлював, що Mathematica визначає велику кількість невикористаних операторів, але ніколи не приділяв цьому великої уваги.
Для довідки, список усіх операторів можна знайти тут у вигляді таблиці пріоритету. Трикутник в останньому стовпчику вказує, чи має цей оператор вбудоване значення чи ні. Хоча не всі ті, які неможливо визначити легко, більшість з них можуть.
Зручно, що є два невикористаних оператора з кодовою точкою менше 256, так що вони можуть бути використані як один байт у вихідному файлі, кодованому ISO 8859-1:
±
(0xB1) можна використовувати як оператор унарного префікса, так і оператор бінарної інфіксації.·
(0xB7) може бути використаний як оператор змінної або n-арфічної інфіксації для n> 2.Однак є ще одна улов: з якоїсь дивної причини для визначення цих операторів вам потрібно один пробіл перед ними, інакше Mathematica намагається проаналізувати множення. При використанні їх вам не потрібні пробіли:
±x_:=2x
x_ ±y_:=x+y
x_ ·y_ ·z_:=x*y+z
Print[±5] (* 10 *)
Print[3±4] (* 7 *)
Print[3·4·5] (* 17 *)
Порівняйте це з:
f@x_:=2x
x_~g~y_:=x+y
h[x_,y_,z_]:=x*y+z
Print[f@5] (* 10 *)
Print[3~g~4] (* 7 *)
Print[h[x,y,z]] (* 17 *)
Таким чином, це зберігає один байт при визначенні функції та два байти при його використанні. Зауважте, що визначення ·
не буде економити байти для чотирьох операндів і почне витрачати байти для більшої кількості операндів, але використання все одно може економити байти, залежно від пріоритету операторів, що використовуються в аргументах. Також добре зазначити, що ви можете дешево визначити різноманітну функцію, яку потім можна назвати набагато ефективніше:
x_ ·y__:={y}
Print[1·2·3·4·5] (* {2, 3, 4, 5} *)
Але зауважте, що не можна легко викликати ці різноманітні функції одним аргументом. (Ви можете зробити CenterDot[x]
або , ##&[]·x
але якщо ви на самому справі потрібно , що є хороший шанс , що ви краще інше рішення.)
Звичайно, це не економить нічого для рішень, де недостатньо називати функцію, але іноді потрібно визначити допоміжні функції для подальшого використання, а іноді коротше визначити названі функції, наприклад встановити різні визначення для різних параметрів. У таких випадках використання оператора натомість може заощадити пристойну кількість байтів.
Зауважте, що для використання цих закодованих файлів ISO 8859-1 потрібно $CharacterEncoding
встановити сумісне значення, наприклад за замовчуванням для Windows WindowsANSI
. У деяких системах ці параметри за замовчуванням, до UTF-8
яких не вдасться прочитати ці кодові точки з одиничних байтів.
Наївний підхід вибирати між y
і z
, залежно від того, x
є 0
чи 1
є
If[x<1,y,z]
Однак існує коротший шлях:
y[z][[x]]
Це працює , тому що [[0]]
дає Head
вираження, в даному випадку y
, в той час як [[1]]
раз дає перший елемент - в цьому випадку перший аргумент, z
.
Ви навіть можете використовувати це для вибору між більш ніж двома значеннями:
u[v,w][[x]]
Зауважте, що це не буде працювати, якщо u
це функція, яка насправді щось оцінює. Важливо, щоб Mathematica тримався u[v,w]
таким, яким він є. Однак це працює в більшості випадків, включаючи, якщо u
a - це число, рядок або список.
Кредити на цей трюк йдуть на алефальфу - я виявив це в одній з його відповідей.
Якщо x
на базі 1 замість нуля, просто використовуйте
{y,z}[[x]]
або
{u,v,w}[[x]]
У деяких рідкісних випадках можна навіть скористатися тим, що множення не оцінюється за деякими значеннями:
{"abc","def"}[[x]]
("abc""def")[[x]]
Зверніть увагу , що хоча Mathematica буде на самому ділі змінити порядок аргументів, з множення , якщо він залишається невичісленним, тому вище ідентичний з
("def""abc")[[x]]
Length
Це було повністю переписано з деякими пропозиціями від LegionMammal978 та Міші Лаврова. Велике спасибі обом.
У багатьох випадках Length
їх можна трохи скоротити, використовуючи Tr
. Основна ідея полягає в тому, щоб перетворити вхідні дані в список 1
s, так що вони Tr
підсумовують їх, що потім дорівнює довжині списку.
Найпоширеніший спосіб зробити це - використовувати 1^x
(для списку x
). Це працює тому, що Power
є Listable
і 1^n
для більшості атомних значень n
справедливо 1
(включаючи всі числа, рядки та символи). Тож ми вже можемо зберегти один байт за допомогою цього:
Length@x
Tr[1^x]
Звичайно, це передбачає , що x
цей вислів з більш високим пріоритетом , ніж ^
.
Якщо x
містять лише 0
s та 1
s, ми можемо зберегти інший байт, використовуючи Factorial
(якщо x
мати перевагу вище !
):
Length@x
Tr[x!]
У деяких рідкісних випадках x
може бути нижчий пріоритет ніж, ^
але все ж більший пріоритет, ніж множення. У цьому випадку він також матиме нижчий пріоритет ніж @
, тому нам дійсно потрібно порівнювати Length[x]
. Прикладом такого оператора є .
. У цих випадках ви можете зберегти байт за допомогою цієї форми:
Length[x.y]
Tr[0x.y+1]
Нарешті, кілька зауважень щодо того, які списки це працює:
Як було сказано вгорі, це працює в плоских списках, що містять лише цифри, рядки та символи. Однак він також буде працювати над деякими глибшими списками, хоча насправді обчислює щось трохи інше. Для n -D прямокутного масиву, використання Tr
дає вам найкоротший розмір (на відміну від першого). Якщо ви знаєте, що зовнішній вимір є найкоротшим, або ви знаєте, що вони однакові, ніж Tr
-вирази все ще еквівалентні Length
.
Length@x == Tr[1^x]
. Слід працювати з більшістю списків.
Tr[x!]
замість того, Tr[1^x]
щоб зберегти один байт у спеціальному випадку, де x
містяться лише нулі та одиниці.
Вивчіть рекурсивні рішення - Mathematica є багатопарадигмою, але функціональний підхід часто є найбільш економічним. NestWhile
може бути дуже компактним рішенням проблем пошуку NestWhileList
та FoldList
є потужним, коли потрібно повернути або обробити результати проміжних ітерацій. Map (/@)
, Apply (@@, @@@)
, MapThread
, І на насправді все на Вольфрам Функціональної програмування сторінки документації є сильнодіючим матеріалом.
Скорочена форма для збільшення / зменшення - Наприклад, замість цього While[i<1,*code*;i++]
ви можете робитиWhile[i++<1,*code*]
Не забувайте, що ви можете попередньо збільшити / зменшити - Наприклад, --i
замість i--
. Це іноді може заощадити кілька байтів у навколишньому коді, усунувши підготовчу операцію.
Наслідок №5 Девіда Каррахера: коли одна і та ж функція використовується багато разів, присвоєння символу їй може зберегти байти. Наприклад, якщо ви використовуєте ToExpression
4 рази в розчині, t=ToExpression
ви можете використовувати його t@*expression*
згодом. Однак перш ніж зробити це, подумайте, чи багаторазове застосування однієї і тієї ж функції вказує на можливість більш економічного рекурсивного підходу.
{}
якщо ви використовуєте @@@
.У деяких випадках ви можете зустріти такий вираз:
f@@@{{a,b},{c,d}}
Зменшити байти можна, записавши:
f@@@{a|b,c|d}
Alternatives
має дуже низький пріоритет, тому взагалі нормально писати вирази (помітним винятком є чисті функції; використовувати їх можна лише в крайньому лівому елементі Alternatives
).
f@@@{f@a|b~g~1,#^2&@c|d@2}
Зауважте, що f@@a|b|c
(замість f@@{a,b,c}
) не працює, оскільки Apply
має вищий пріоритет ніж Alternative
.
У цьому випадку вам слід просто скористатися f@@{a,b,c}
.
Форми оператора
Mathematica 10 підтримує так звані "форми операторів", що в основному означає, що деякі функції можуть бути викривлені. Запропонування функції - це створення нової функції шляхом фіксації одного з її операторів. Скажіть, ви використовуєте SortBy[list, somereallylongfunction&]
безліч різних list
s. Перед тим , ви , ймовірно , були б призначені SortBy
на s
і чисту функцію f
So
s=SortBy;
f=somereallylongfunction&;
list1~s~f;
list2~s~f;
list3~s~f;
Тепер ви можете порізати каррі SortBy
, а значить, тепер можете це зробити
s=SortBy[somereallylongfunction&];
s@list1;
s@list2;
s@list3;
Один і той же твір для багатьох інших функцій, які приймають список або аргумент функції, в тому числі (але не обмежуючись ними) Select
, Map
, Nearest
і т.д.
ybeltukov на Mathematica.SE зміг створити повний перелік таких :
{"AllTrue", "AnyTrue", "Append", "Apply", "AssociationMap", "Cases",
"Count", "CountDistinctBy", "CountsBy", "Delete", "DeleteCases",
"DeleteDuplicatesBy", "Extract", "FirstCase", "FirstPosition",
"FreeQ", "GroupBy", "Insert", "KeyDrop", "KeyExistsQ", "KeyMap",
"KeySelect", "KeySortBy", "KeyTake", "Map", "MapAt", "MapIndexed",
"MatchQ", "MaximalBy", "MemberQ", "Merge", "MinimalBy", "NoneTrue",
"Position", "Prepend", "Replace", "ReplacePart", "Scan", "Select",
"SelectFirst", "SortBy", "StringCases"}
Склад і RightComposition
Існують нові скорочення для Composition
( @*
) та RightComposition
( /*
). Очевидно надуманий приклад, коли вони можуть зберігати символи, видно у наступних трьох еквівалентних рядках
Last@Range@# & /@ Range[5]
Last@*Range /@ Range[5]
Range /* Last /@ Range[5]
Не потрібно такого коду:
f[]:=DoSomething[1,2]
(*...*)
f[]
(*...*)
f[]
Ви можете просто використовувати змінну, :=
щоб примусити переоцінити праву частину:
f:=DoSomething[1,2]
(*...*)
f
(*...*)
f
Це також означає, що ви можете псевдонімувати будь-яку дію, яку виконуєте часто (навіть якщо це просто щось на зразок n++
) одному символу вартістю 5 байт. Тож у випадку, коли n++
він окупається після четвертого використання:
n++;n++;n++;n++
f:=n++;f;f;f;f
%
щоб отримати безкоштовну зміннуЦей підхід застосовується лише в тому випадку, якщо можна припустити середовище REPL Mathematica. %
не визначено, коли код запускається як сценарій.
Коли ви можете використовувати функції REPL, не робіть цього:
a=someLongExpression;some[other*a,expression@a,using^a]
Натомість пам’ятайте, що Mathematica зберігає останній оцінений вираз (новий рядок) у %
:
someLongExpression;
some[other*%,expression@%,using^%]
Доданий новий рядок коштує байт, але ви економите два, видаляючи a=
, так що в цілому це економить один байт.
У деяких випадках (наприклад, коли ви хочете роздрукувати значення в a
будь-якому випадку), ви навіть можете відмовитися від цього ;
, зберігаючи два байти:
someLongExpression
some[other*%,expression@%,using^%]
Один або два байти можуть здатися досить незначними, але це важливий випадок, оскільки це робить витягування повторних виразів (що є дуже поширеною технікою) набагато кориснішими при гольфі:
Звичайна техніка вилучення повторних виразів коштує чотири байти накладних витрат, які потрібно зберегти шляхом подальшого використання виразу. Ось коротка таблиця мінімальної кількості використання виразу (за довжиною виразу) для вилучення в названу змінну, щоб зберегти що-небудь:
Length Min. Uses
2 6
3 4
4 3
5 3
6 2
... 2
За допомогою безіменної змінної можна значно частіше зберегти пару байтів:
When ; is required When ; can be omitted
Length Min. Uses Length Min. Uses
2 5 2 4
3 3 3 3
4 3 4 2
5 2 ... 2
... 2
Я не думаю, що %%
і %n
не можна використовувати для гри в гольф, тому що якщо ви не використовуєте їх принаймні двічі, ви можете просто поставити вираз там, де це потрібно. І якщо ви використовуєте його двічі, додатковий символ у назві змінної скасовує заощадження від їх опущення x=
.
Це, по суті, є наслідком цього підказу, але це досить поширене завдання, на який я думаю, що воно вимагає власної відповіді.
Наївний спосіб перевірити, чи потрібен список, - це використовувати
OrderedQ@a
Ми можемо зробити один байт краще за допомогою
Sort@a==a
Однак це не працює, якщо у нас вже немає тієї речі, яку ми хочемо перевірити в змінній. (Нам знадобиться щось на кшталт того, Sort[a=...]==a
що непотрібно довго.) Однак є й інший варіант:
#<=##&@@a
Найкраще, що це можна використати для перевірки, чи введений вхід зворотно відсортований за тим самим числом байтів:
#>=##&@@a
Ще один байт може бути збережений, якщо: a) ми знаємо, що елементи списку є різними, і b) ми знаємо нижню межу між 0 і 9 (включно; або верхню межу для зворотного відсортованого порядку):
0<##&@@a
5>##&@@a
Щоб зрозуміти, чому це працює, перегляньте "Послідовність аргументів" у підказці, пов'язаній вгорі.
##>0&@@a
. Подібна для верхньої межі для відсортованої.
Замість StringRepeat[str,n]
використання (0Range[n]+str)<>""
. Або якщо str
це не залежить від жодних аргументів слота, ще краще, Array[str&,n]<>""
як передбачено цією порадою.
StringRepeat[s,n+1]
використання Array[s&,n]<>s
(навіть коли у вас вже є n+1
змінна).
Table[str,n]<>""
Якщо вам потрібен список номерів, відсортованих у зворотному порядку, не використовуйте
Reverse@Sort@x
але
-Sort@-x
щоб зберегти шість байтів. Сортування за мінус-значенням також корисне для SortBy
сценаріїв:
Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]
-Sort@-x
?
Ви можете наклеїти вираз, в Break
якому можна зберегти один або два символи. Приклад ( інші деталі, не зрозумілі для гостроти ):
result = False;
Break[]
можна перетворити на
Break[result = False]
щоб зберегти один символ. Якщо виразний вираз не має нижчого пріоритету, ніж функція програми, ви можете навіть зберегти інший символ:
Print@x;
Break[]
можна перетворити на
Break@Print@x
Незважаючи на недокументування, аргумент, Break
здається, повертається оточуючим циклом, що потенційно може призвести до ще більшої економії.
Щоб видалити пробіл із рядка s
, використовуйте
StringSplit@s<>""
Тобто, використовуйте StringSplit
за замовчуванням (розділити на компоненти, що не містять пробілів) та просто з'єднайте їх разом. Це, мабуть, все-таки найкоротше, якщо ви хочете позбутися будь-якого іншого символу чи підрядка:
s~StringSplit~"x"<>""
Range
Дуже поширене завдання - застосувати якусь функцію до всіх чисел від 1 до а n
(як правило, дається як вхід). По суті є 3 способи зробити це (використовуючи неназвану функцію ідентичності як приклад):
#&/@Range@n
Array[#&,n]
Table[i,{i,n}]
Я схильний їхати за першим (з будь-якої причини), але це рідко є найкращим вибором.
Array
замість цьогоВищеописаний приклад показує, що використання Array
має однаковий кількість байтів. Однак має перевагу, що це єдиний вираз. Зокрема, якщо ви хочете додатково обробити результат за допомогою функції, f
ви можете використовувати нотацію префікса, яка зберігає байт над Range
:
f[#&/@Range@n]
f@Array[#&,n]
Крім того, ви можете пропустити дужки навколо своєї неназваної функції, яка, можливо, вам знадобиться Range
, наприклад
15/(#&)/@Range@n
15/Array[#&,n]
Якщо ви не хочете більше використовувати його (або з оператором, який має менший пріоритет), ви можете замість цього записати Array
себе у позначення інфіксації, а також зберегти байт:
#&/@Range@n
#&~Array~n
Отже, Array
майже напевно краще Range
.
Table
замість цьогоТепер таблиця повинна містити 3 байти або, принаймні, 2, коли позначення інфікса є опцією:
#&/@Range@n
i~Table~{i,n}
Якщо ви не використовуєте позначення infix, Table
ви можете опустити круглі дужки, якщо ваша функція складається з кількох операторів:
(#;#)&/@Range@n
Table[i;i,{i,n}]
Це ще довше, але дає додаткові заощадження у випадку, зазначеному нижче.
Реальну економію випливає з того, що Table
дає ім'я працюючої змінної не слід відкидати. Часто у вас вкладені неназвані функції, де ви хочете використовувати зовнішню змінну всередині однієї із внутрішніх функцій. Коли це відбувається, Table
він коротший, ніж Range
:
(i=#;i&[])&/@Range@n
Table[i&[],{i,n}]
i&[]~Table~{i,n}
Ви не тільки зберігаєте символи для присвоєння i
, ви також можете звести функцію до одного оператора в процесі, що дозволяє використовувати позначення інфіксації поверх нього. Для довідки, Array
в цьому випадку також довше, але все ж коротше, ніж Range
:
(i=#;i&[])&~Array~n
Range
?Всякий раз, коли вам не потрібен виклик функції для обробки значень, наприклад, коли відображення може виконуватися за допомогою векторизованої операції. Наприклад:
5#&~Array~n
5Range@n
#^2&~Array~n
Range@n^2
Звичайно, він також коротший, якщо ви взагалі не хочете відображати будь-яку функцію, наприклад
Mean@Array[#&,n]
Mean@Range@n
f/@Range[x]
регулярно використовує ...
Деякі такі конструкції, як i=1;While[cond[i],i++]
добре, є, але є альтернатива, яка на два байти коротша:
1//.i_/;cond[i]:>i+1
Вищевказаний код неодноразово замінює номер i
на i+1
той час, коли він відповідає умові cond[i]
. У цьому випадку i
починається з 1
.
Зауважте, що за замовчуванням максимальна кількість ітерацій становить 2 ^ 16 (= 65536). Якщо вам потрібно більше ітерацій, While
було б краще. ( MaxIterations->∞
занадто довго)
Іноді можна замінити If
логічним оператором.
Наприклад, скажімо, що ви хочете зробити функцію, яка перевіряє, чи є число простим, і друкувати 2*(number) - 1
, чи це правда:
If[PrimeQ@#,Print[2#-1]]&
Це коротше, якщо ви використовуєте &&
замість цього:
PrimeQ@#&&Print[2#-1]&
Навіть коли у вас є кілька виразів, ви все одно зберігаєте байти:
If[PrimeQ@#,a++;Print[2#-1]]&
PrimeQ@#&&a++&&Print[2#-1]&
(* or *)
PrimeQ@#&&(a++;Print[2#-1])&
Ви можете використовувати ||
для випадків, коли ви хочете, щоб ця умова була False
:
If[!PrimeQ@#,Print[2#-1]]&
(* or *)
If[PrimeQ@#,,Print[2#-1]]&
(* can become *)
PrimeQ@#||Print[2#-1]&
Ці хитрощі працюють тому, що логічні оператори можуть бути короткозамикаючими ; другий аргумент і надалі навіть не повинні бути дійсними булевими виразами.
Звичайно, це не спрацьовує, якщо вам потрібно повернути значення If
або коли вам потрібні і правдиві, і хибні аргументи If
.
Ось список із завантаженням форм введення оператора, який може скоротити багато речей. Про деякі з них згадувалося в інших публікаціях, але список довгий, і я завжди здивований, коли знайшов там кілька нових речей:
Optional (:)
Optional (:)
може використовуватися для розширення списків у замінниках, не визначаючи окремого правила розширення.
Ця моя відповідь і ця відповідь @ngenisis - приклади.
Використання
... /. {p___, a_: 0, b_, q___} /; cond[b] :> ...
Вищезазначена заміна спочатку використовує шаблон {p___, a_, b_, q___}
і знаходить відповідність такому, що b
відповідає певній умові.
Коли такої відповідності не знайти, вона вимикає a_
і замість цього шукає {p___, b_, q___}
. a
не включається до пошуку і вважається, що має значення 0
.
Зверніть увагу, що пошук другого шаблону спрацював би лише на b
тому, що відбувається на початку списку; якщо b
значення, що задовольняє умові, знаходиться посередині, то {p___, a_, b_, q___}
(яке має більший пріоритет) замість нього.
Заміна еквівалентна попередньому а, 0
коли b
на початку списку виникає задовольняюча умова. (тобто немає необхідності визначати окреме правило, {b_, q___} /; cond[b] :> ...
)
Для кодового гольфу Function
найчастіше посилання на чисті аргументи використовують Slot
s; наприклад, #
для першого аргументу, #2
для другого тощо (див. цю відповідь для більш детальної інформації).
У багатьох випадках вам захочеться гніздо Function
с. Наприклад, 1##&@@#&
це a, Function
що приймає список як перший аргумент і виводить твір його елементів. Ось ця функція в TreeForm
:
Аргументи, передані на верхній рівень, Function
можуть заповнювати лише Slot
s та SlotSequence
s, присутні на верхньому рівні, що в цьому випадку означає, що SlotSequence
внутрішній Function
не матиме жодного способу доступу аргументів до верхнього рівня Function
.
Однак у деяких випадках вам може знадобитися Function
вкладене всередині іншого, Function
щоб мати можливість посилати аргументи на зовнішні Function
. Наприклад, ви можете хотіти щось на зразок Array[fun,...]&
, коли функція fun
залежить від аргументу до верхнього рівня Function
. Щодо конкретності, скажімо, що fun
має дати залишок квадрата вхідного модуля вхід до верхнього рівня Function
. Одним із способів досягти цього є присвоєння аргументу верхнього рівня змінній:
(x=#;Array[Mod[#^2,x]&,...])&
Де б не x
з'явилося у внутрішньому Function
Mod[#^2,x]&
, він буде посилатися на перший аргумент до зовнішнього Function
, тоді як #
буде посилатися на перший аргумент до внутрішнього Function
. Кращим підходом є використання того факту, який Function
має дві форми аргументів, де перший аргумент є символом або списком символів, які представлятимуть названі аргументи Function
(на відміну від неназваних Slot
s). Це в результаті заощаджує нам три байти в цьому випадку:
xArray[Mod[#^2,x]&,...]
це трибайтовий символ приватного використання, що U+F4A1
представляє оператор бінарної інфіксації \[Function]
. Ви також можете використовувати двійкову форму Function
в іншому Function
:
Array[xMod[x^2,#],...]&
Це рівнозначно вище. Причина полягає в тому, що якщо ви використовуєте названі аргументи, тоді Slot
s і SlotSequences
вважається, що вони належать до наступного Function
вище, який не використовує названі аргументи.
Тепер це те, що ми можемо гніздитися Function
таким чином, не означає, що ми завжди повинні. Наприклад, якщо ми хотіли вибрати ті елементи списку, які менші за вхідні дані, ми можемо спокуситися зробити щось на кшталт наступного:
Select[...,xx<#]&
Насправді використання було б коротшим Cases
та уникнути потреби Function
цілком вкладатись :
Cases[...,x_/;x<#]&
Ви можете зберегти байт, обійшовши Prepend
або PrependTo
:
l~Prepend~x
{x}~Join~l
{x,##}&@@l
або
l~PrependTo~x
l={x}~Join~l
l={x,##}&@@l
На жаль, це не допомагає для більш поширеного Append
, який, здається, є найкоротшим еквівалентом Array.push()
інших мов.
BlockMap
є Partition
+Map
Ця порада також може бути названа "Прочитайте всі нотатки до випуску". (Для довідки, ось примітки до випуску 10.2 та ось сьогоднішній 10.3 випуск .)
У будь-якому випадку, навіть незначні випуски містять безліч нових функцій, а однією з більш корисних (для гольфу) з 10.2 є нова BlockMap
функція. Він по суті поєднує Partition
і Map
, що чудово підходить для гравців у гольф, тому що Partition
використовується досить часто, і це дійсно прикро довга назва функції. Нова функція не скорочується Partition
сама по собі, але коли ви хочете зіставити функцію на розділи (що, мабуть, трапляється частіше, ніж ні), тепер ви можете зберегти байт або два:
#&/@l~Partition~2
BlockMap[#&,l,2]
#&/@Partition[l,3,1]
BlockMap[#&,l,3,1]
Економія стає ще більшою, коли нова позиція неназваної функції дозволяє вам зберегти деякі круглі дужки:
#&@@(#&/@Partition[l,3,1])
#&@@BlockMap[#&,l,3,1]
На жаль, я не маю уявлення, чому також не додав, BlockApply
поки вони були на цьому ...
Також зауважте, що BlockMap
не підтримує 4-й параметр, з яким можна використовувати Partition
циклічний список:
Partition[Range@5, 2, 1, 1]
(* Gives {{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 1}} *)
BlockMap[f, Range@5, 2, 1, 1]
(* Nope... *)
Якщо ваша відповідь закінчується використанням одних і тих же функцій або виразів кілька разів, можливо, ви захочете розглянути можливість їх зберігання у змінних.
Якщо вираз має довжину, l
і ви використовуєте його в n
рази, воно, як правило, використовує l * n
байти.
Однак якщо ви зберігаєте його у змінній довжини-1, він займе лише 3 + l + n
байти (або 2 + l + n
байти, якщо ви призначите змінну там, де вам не знадобиться CompoundExpression (;)
чи в дужках).
Наприклад, розглянемо просту проблему, виявивши близнюків-близнюків менше ніж N.
Можна було б написати це 54-байтове рішення:
Select[Range@#,PrimeQ@#&&(PrimeQ[#+2]||PrimeQ[#-2])&]&
У цьому прикладі функція PrimeQ
використовується тричі.
Присвоївши PrimeQ
ім'я змінної, кількість байтів можна зменшити. Обидва наступні - 48 байт (54 - 6 байт):
Select[p=PrimeQ;Range@#,p@#&&(p[#+2]||p[#-2])&]&
Select[Range@#,(p=PrimeQ)@#&&(p[#+2]||p[#-2])&]&
Sort
замістьSortBy
Для таких списків, як list = {{1, "world"}, {0, "universe"}, {2, "country"}}
, наприклад , наступні три твердження майже рівнозначні.
SortBy[list,#[[1]]&]
list~SortBy~First
Sort@list
Select
іSortBy
Іноді нам потрібно вибирати записи з більшого набору і сортувати їх, щоб знайти мінімум / максимум. За певних обставин дві операції можна було об'єднати в одну.
Наприклад, як мінімум, наступні два твердження майже рівнозначні.
SortBy[Select[l,SomeFormula==SomeConstant&],SortValue&]
SortBy[l,SortValue+99!(SomeFormula-SomeConstant)^2&]
і
SortBy[Select[l,SomeFormula!=SomeConstant&],SortValue&]
SortBy[l,SortValue+1/(SomeFormula-SomeConstant)&]
1/0
є ComplexInfinity
, яка "більша", ніж усі реальні числа.
Наприклад, для списку ключових значень:
{SortValue,#}&/@SortBy[Select[l,SomeFormula==SomeConstant],SortValue&]
Sort[{SortValue+99!(SomeFormula-SomeConstant)^2,#})&/@l]
Array
с##&
Використовуючи багатовимірний масив для обчислення списку результатів, який потрібно вирівняти, використовуйте ##&
як четвертий аргумент. Це замінює заголовки масиву замість ##&
(еквівалентно Sequence
) List
, тому кінцевим результатом буде (плоскість) Sequence
результатів.
У двох вимірах порівняйте
{Array[f,dims,origin,##&]}
Join@@Array[f,dims,origin]
Звичайно,
Join@@Array[f,dims]
все ж на 2 (або 3, якщо можна використовувати нотацію інфіксації) байт коротше, ніж
{Array[f,dims,1,##&]}
.
У трьох і більше вимірах {Array[f,dims,origin,##&]}
завжди коротший, ніж альтернативний, навіть якщо походження дорівнює 1.
{Array[f,dims,1,##&]}
f~Array~dims~Flatten~2
Значення за замовчуванням ефективно обробляють відсутні аргументи шаблону. Наприклад, якщо ми хочемо вирівняти відповідність Exp[c_*x]
правилу для будь-якого значення c
, наївний
Exp[x] + Exp[2x] /. {Exp[c_*x] -> f[c], Exp[x] -> f[1]}
(* f[1] + f[2] *)
використовує набагато більше байтів, ніж якщо ми використовуємо значення за замовчуванням, c
коли воно відсутнє:
Exp[x] + Exp[2 x] /. Exp[c_.*x] -> f[c]
(* f[1] + f[2] *)
Використання по замовчуванням позначається крапкою після шаблону: c_.
.
Значення по замовчуванням пов'язані з операціями: в наведеному вище прикладі, операція Times
в c_.*x
і немає значення c_
, таким чином , беруться зі значення за замовчуванням , пов'язане з Times
, що 1. Для Plus
, значення за замовчуванням дорівнює 0:
Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(* f[0] + f[2] *)
Для Power
експонентів типовим є 1:
x + x^2 /. x^n_. -> p[n]
(* p[1] + p[2] *)
(Norm[#-#2]&)
замістьEuclideanDistance
.