Поради щодо гольфу в Mathematica


41

Які загальні поради щодо гольфу в Mathematica? Я шукаю ідеї, які можна застосувати до коду проблем із гольфом взагалі, які принаймні дещо характерні для Mathematica (наприклад, "видалити коментарі" - це не відповідь).

Відповіді:


30

Підказки нижче відрізняються від найекономічніших до найчастіше використовуваних:

  1. Використовуйте команди високого рівня Mathematica, де це можливо, навіть об'ємні:

  2. Використання Graphics and Textдля мистецтва Ascii: наприклад, програмування зірок! і побудувати аналоговий годинник

  3. Виділені символи:

    • логіка та встановлення символів операцій замість їх довгих форм імен: ⋂, ⋃, ∧, ∨

    • Mapі Apply: /@, //@. @@,@@@

  4. Позначення префікса та інфіксації:

    • Print@"hello" замість Print["hello"]

    • a~f~b замість f[a,b]

  5. Якщо функція використовується лише один раз, чиста функція може економити символ або два.

  6. Приєднання рядків до списку. ""<>{"a","b","c"}замістьStringJoin@{"a","b","c"}

  7. Використовуйте функції, що можна відстежувати. Чим довші списки, тим краще.

    {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}}


2
Це завжди коротше писати (Norm[#-#2]&)замість EuclideanDistance.
користувач202729

32

Деякі вбудовані функції з довгими іменами можна замінити коротшими виразами.

Наприклад:

  • 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є список 0s і 1s
  • Complex@z=> {1,I}.zде zперелік форми{x,y}

5
@belisarius 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}}]
алефальфа

2
Я думаю, що ваш Foldтрюк FromDigitsтакож працює для будь-якої іншої бази, крім 10. Наприклад FromDigits[n,5]-> Fold[4#+##&,n](з бонусом збереження додаткового байта для баз 100і 1000).
Мартін Ендер

1
@ mbomb007 3 байти в UTF-8. Насправді цей персонаж є U+F3C7.
алефальфа

1
Нарешті я встановив 10.3. Якщо ми розглядаємо повні програми, я не думаю Echo, що це варіант, оскільки він друкує >>(і пробіл) на STDOUT перед друком фактичного рядка.
Мартін Ендер

2
Бо Complex[x,y] => {1,I}.{x,y}я думаю x+y*I, що набагато коротше з тим же ефектом?
Шиеру Асакото

22

Списки з повторними значеннями

Це досить поширений вектор для роботи з:

{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) є неявними Nulls. Крім того, множення Listable, і 0*xє 0практично для будь-якого x(за винятком речей , як Infinityі Indeterminate), так ось що відбувається:

  0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}

Для списків 1s можна скористатися аналогічним трюком, використовуючи правила експоненції. Існує два різні способи збереження байтів, якщо 1у списку є принаймні три s:

{1,1,1}
1^{,,}
{,,}^0

7
+1; це лише показує, що хоча Mathematica може мати вбудований для всього, гольф в ньому може бути справжньою проблемою.
LegionMammal978

Якщо ви хочете масив, який в кінцевому рахунку заповнений 1s, то 1^{,,,}на один байт менше, ніж 0{,,,}+1.
Міша Лавров

@MishaLavrov О, хороший улов. Це робить його коротшим на три значення, і ви також можете використовувати {,,}^0. Я відредагую публікацію.
Мартін Ендер

19

Знайте свої аргументи чистої функції

Під час коду для гольфу ви часто використовуєте функціональний підхід, коли ви використовуєте анонімні (чисті) функції зі &скороченим синтаксисом. Існує дуже багато різних способів отримати доступ до аргументів такої функції, і ви часто можете поголити пару байтів, добре зрозумівши свої можливості.

Доступ до одиничних аргументів

Ви, мабуть, знаєте це, якщо раніше ви використовували чисті функції. П - й аргумент називається #n, і #діє в якості псевдоніма для #1. Отже, якщо, скажімо, ви хочете написати функцію, яка приймає в якості параметрів іншу функцію та її аргумент (щоб передати аргумент цій функції), використовуйте

#@#2&

Це не працює з негативними номерами (наприклад, ви можете використовуватись для доступу до списків).

Доступ до названих аргументів (новий у V10)

Однією з головних нових мовних особливостей у Mathematica 10 є Associations, які в основному є ключовими картами з довільними типами ключів, написаними як

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

Продовжуйте експериментувати з цим, і повідомте мені, чи знайдете ви якісь інші корисні або особливо цікаві програми!


15

Sqrt@2або 2^.5=>√2

a[[1]]=>a〚1〛

#+#2&=>+##&

Flatten@a=> Join@@a(іноді)

Function[x,x^2]=> xx^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


1
Зауважте, що при вимірюванні байтами використовується і бере 3 байти кожен (припустимо, UTF8)
user202729

12

Оператори як функції

Надихнувшись нещодавним відкриттям Денніса для Джулії, я подумав, що я буду розбиратися в цьому для 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яких не вдасться прочитати ці кодові точки з одиничних байтів.


Це дійсно чудово, я не знав, що Mathematica має список операторів і навіть включив їхній пріоритет. Ці два оператори, яких ти знайшов, я впевнений, стануть у нагоді.
миль

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]таким, яким він є. Однак це працює в більшості випадків, включаючи, якщо ua - це число, рядок або список.

Кредити на цей трюк йдуть на алефальфу - я виявив це в одній з його відповідей.

Якщо xна базі 1 замість нуля, просто використовуйте

{y,z}[[x]]

або

{u,v,w}[[x]]

У деяких рідкісних випадках можна навіть скористатися тим, що множення не оцінюється за деякими значеннями:

{"abc","def"}[[x]]
("abc""def")[[x]]

Зверніть увагу , що хоча Mathematica буде на самому ділі змінити порядок аргументів, з множення , якщо він залишається невичісленним, тому вище ідентичний з

("def""abc")[[x]]

8

Альтернативи Length

Це було повністю переписано з деякими пропозиціями від LegionMammal978 та Міші Лаврова. Велике спасибі обом.

У багатьох випадках Lengthїх можна трохи скоротити, використовуючи Tr. Основна ідея полягає в тому, щоб перетворити вхідні дані в список 1s, так що вони Trпідсумовують їх, що потім дорівнює довжині списку.

Найпоширеніший спосіб зробити це - використовувати 1^x(для списку x). Це працює тому, що Powerє Listableі 1^nдля більшості атомних значень nсправедливо 1(включаючи всі числа, рядки та символи). Тож ми вже можемо зберегти один байт за допомогою цього:

Length@x
Tr[1^x]

Звичайно, це передбачає , що xцей вислів з більш високим пріоритетом , ніж ^.

Якщо xмістять лише 0s та 1s, ми можемо зберегти інший байт, використовуючи Factorial(якщо xмати перевагу вище !):

Length@x
Tr[x!]

У деяких рідкісних випадках xможе бути нижчий пріоритет ніж, ^але все ж більший пріоритет, ніж множення. У цьому випадку він також матиме нижчий пріоритет ніж @, тому нам дійсно потрібно порівнювати Length[x]. Прикладом такого оператора є .. У цих випадках ви можете зберегти байт за допомогою цієї форми:

Length[x.y]
Tr[0x.y+1]

Нарешті, кілька зауважень щодо того, які списки це працює:

Як було сказано вгорі, це працює в плоских списках, що містять лише цифри, рядки та символи. Однак він також буде працювати над деякими глибшими списками, хоча насправді обчислює щось трохи інше. Для n -D прямокутного масиву, використання Trдає вам найкоротший розмір (на відміну від першого). Якщо ви знаєте, що зовнішній вимір є найкоротшим, або ви знаєте, що вони однакові, ніж Tr-вирази все ще еквівалентні Length.


3
Просто знайти ще коротше рішення: Length@x == Tr[1^x]. Слід працювати з більшістю списків.
LegionMammal978

@ LegionMammal978 це дивовижно, дякую :) Я скоро її відредагую.
Мартін Ендер

1
Зараз я вже два рази використовував Tr[x!]замість того, Tr[1^x]щоб зберегти один байт у спеціальному випадку, де xмістяться лише нулі та одиниці.
Міша Лавров

@MishaLavrov Це справді акуратно! :)
Мартін Ендер

7
  1. Вивчіть рекурсивні рішення - Mathematica є багатопарадигмою, але функціональний підхід часто є найбільш економічним. NestWhileможе бути дуже компактним рішенням проблем пошуку NestWhileListта FoldListє потужним, коли потрібно повернути або обробити результати проміжних ітерацій. Map (/@), Apply (@@, @@@), MapThread, І на насправді все на Вольфрам Функціональної програмування сторінки документації є сильнодіючим матеріалом.

  2. Скорочена форма для збільшення / зменшення - Наприклад, замість цього While[i<1,*code*;i++]ви можете робити
    While[i++<1,*code*]

  3. Не забувайте, що ви можете попередньо збільшити / зменшити - Наприклад, --iзамість i--. Це іноді може заощадити кілька байтів у навколишньому коді, усунувши підготовчу операцію.

  4. Наслідок №5 Девіда Каррахера: коли одна і та ж функція використовується багато разів, присвоєння символу їй може зберегти байти. Наприклад, якщо ви використовуєте ToExpression4 рази в розчині, t=ToExpressionви можете використовувати його t@*expression*згодом. Однак перш ніж зробити це, подумайте, чи багаторазове застосування однієї і тієї ж функції вказує на можливість більш економічного рекурсивного підходу.


MapThreadчасто можна замінити на \[Transpose]. ТІО .
користувач202729

7

Не використовуйте, {}якщо ви використовуєте @@@.

У деяких випадках ви можете зустріти такий вираз:

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}.


6

Математика лише 10

Форми оператора

Mathematica 10 підтримує так звані "форми операторів", що в основному означає, що деякі функції можуть бути викривлені. Запропонування функції - це створення нової функції шляхом фіксації одного з її операторів. Скажіть, ви використовуєте SortBy[list, somereallylongfunction&]безліч різних lists. Перед тим , ви , ймовірно , були б призначені SortByна sі чисту функцію fSo

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]

5

Не пишіть функції 0-аргументів

Не потрібно такого коду:

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

5

Використовуйте, %щоб отримати безкоштовну змінну

Цей підхід застосовується лише в тому випадку, якщо можна припустити середовище 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=.


Зауважте, що він не працює в режимі сценарію.
алефальфа

@alephalpha Що таке режим сценарію?
Мартін Ендер


@alephalpha О так, я відключив свій мозок на секунду ... так що це означає, що його взагалі не можна використовувати, якщо не можна припустити, що середовище REPL.
Мартін Ендер

5

Перевірка, чи список відсортований

Це, по суті, є наслідком цього підказу, але це досить поширене завдання, на який я думаю, що воно вимагає власної відповіді.

Наївний спосіб перевірити, чи потрібен список, - це використовувати

OrderedQ@a

Ми можемо зробити один байт краще за допомогою

Sort@a==a

Однак це не працює, якщо у нас вже немає тієї речі, яку ми хочемо перевірити в змінній. (Нам знадобиться щось на кшталт того, Sort[a=...]==aщо непотрібно довго.) Однак є й інший варіант:

#<=##&@@a

Найкраще, що це можна використати для перевірки, чи введений вхід зворотно відсортований за тим самим числом байтів:

#>=##&@@a

Ще один байт може бути збережений, якщо: a) ми знаємо, що елементи списку є різними, і b) ми знаємо нижню межу між 0 і 9 (включно; або верхню межу для зворотного відсортованого порядку):

0<##&@@a
5>##&@@a

Щоб зрозуміти, чому це працює, перегляньте "Послідовність аргументів" у підказці, пов'язаній вгорі.


Як альтернативи, (строге) Нижня межа для зворотного сортування і роботи: ##>0&@@a. Подібна для верхньої межі для відсортованої.
користувач202729

@ user202729 О, хороший момент, не соромтесь редагувати (інакше я спробую це зробити у вихідні, якщо я пам’ятаю).
Мартін Ендер

5

Повторення рядка

Замість StringRepeat[str,n]використання (0Range[n]+str)<>"". Або якщо strце не залежить від жодних аргументів слота, ще краще, Array[str&,n]<>""як передбачено цією порадою.


1
Висновок: замість StringRepeat[s,n+1]використання Array[s&,n]<>s(навіть коли у вас вже є n+1змінна).
Мартін Ендер

Краще,Table[str,n]<>""
attinat

5

Якщо вам потрібен список номерів, відсортованих у зворотному порядку, не використовуйте

Reverse@Sort@x

але

-Sort@-x

щоб зберегти шість байтів. Сортування за мінус-значенням також корисне для SortByсценаріїв:

Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]

2
Про що -Sort@-x?
JungHwan Min

1
@JungHwanMin О, так, так, це набагато краще. :)
Мартін Ендер

4

Ви можете наклеїти вираз, в Breakякому можна зберегти один або два символи. Приклад ( інші деталі, не зрозумілі для гостроти ):

result = False;
Break[]

можна перетворити на

Break[result = False]

щоб зберегти один символ. Якщо виразний вираз не має нижчого пріоритету, ніж функція програми, ви можете навіть зберегти інший символ:

Print@x;
Break[]

можна перетворити на

Break@Print@x

Незважаючи на недокументування, аргумент, Breakздається, повертається оточуючим циклом, що потенційно може призвести до ще більшої економії.


4

Щоб видалити пробіл із рядка s, використовуйте

StringSplit@s<>""

Тобто, використовуйте StringSplitза замовчуванням (розділити на компоненти, що не містять пробілів) та просто з'єднайте їх разом. Це, мабуть, все-таки найкоротше, якщо ви хочете позбутися будь-якого іншого символу чи підрядка:

s~StringSplit~"x"<>""

4

Альтернативи 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]регулярно використовує ...
LegionMammal978

4

Знаходження найменшого числа, яке задовольняє умові

Деякі такі конструкції, як i=1;While[cond[i],i++]добре, є, але є альтернатива, яка на два байти коротша:

1//.i_/;cond[i]:>i+1

Вищевказаний код неодноразово замінює номер iна i+1той час, коли він відповідає умові cond[i]. У цьому випадку iпочинається з 1.

Зауважте, що за замовчуванням максимальна кількість ітерацій становить 2 ^ 16 (= 65536). Якщо вам потрібно більше ітерацій, Whileбуло б краще. ( MaxIterations->∞занадто довго)


4

Зловживання оцінкою короткого замикання

Іноді можна замінити 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.


3

Ось список із завантаженням форм введення оператора, який може скоротити багато речей. Про деякі з них згадувалося в інших публікаціях, але список довгий, і я завжди здивований, коли знайшов там кілька нових речей:


Однак це працює лише тоді, коли оператор використовує менше байтів UTF-8.
LegionMammal978

3

Використання 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] :> ...)


3

Знайте, коли (а коли ні) використовувати аргументовані чисті функції

Для кодового гольфу Functionнайчастіше посилання на чисті аргументи використовують Slots; наприклад, #для першого аргументу, #2для другого тощо (див. цю відповідь для більш детальної інформації).

У багатьох випадках вам захочеться гніздо Functionс. Наприклад, 1##&@@#&це a, Functionщо приймає список як перший аргумент і виводить твір його елементів. Ось ця функція в TreeForm:

введіть тут опис зображення

Аргументи, передані на верхній рівень, Functionможуть заповнювати лише Slots та SlotSequences, присутні на верхньому рівні, що в цьому випадку означає, що 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(на відміну від неназваних Slots). Це в результаті заощаджує нам три байти в цьому випадку:

xArray[Mod[#^2,x]&,...]

це трибайтовий символ приватного використання, що U+F4A1представляє оператор бінарної інфіксації \[Function]. Ви також можете використовувати двійкову форму Functionв іншому Function:

Array[xMod[x^2,#],...]&

Це рівнозначно вище. Причина полягає в тому, що якщо ви використовуєте названі аргументи, тоді Slots і SlotSequencesвважається, що вони належать до наступного Functionвище, який не використовує названі аргументи.

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

Select[...,xx<#]&

Насправді використання було б коротшим Casesта уникнути потреби Functionцілком вкладатись :

Cases[...,x_/;x<#]&

2

Ви можете зберегти байт, обійшовши Prependабо PrependTo:

l~Prepend~x
{x}~Join~l
{x,##}&@@l

або

l~PrependTo~x
l={x}~Join~l
l={x,##}&@@l

На жаль, це не допомагає для більш поширеного Append, який, здається, є найкоротшим еквівалентом Array.push()інших мов.


2

Mathematica 10.2: 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... *)

2

Зберігання функцій та виразів у змінній

Якщо ваша відповідь закінчується використанням одних і тих же функцій або виразів кілька разів, можливо, ви захочете розглянути можливість їх зберігання у змінних.

Якщо вираз має довжину, 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])&]&

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]

1

Згладжування 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

1

Значення за замовчуванням

Значення за замовчуванням ефективно обробляють відсутні аргументи шаблону. Наприклад, якщо ми хочемо вирівняти відповідність 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]    *)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.