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


41

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

Ответы:


30

Советы ниже варьируются от самых экономичных до наиболее часто используемых:

  1. По возможности используйте команды высокого уровня Mathematica, даже громоздкие:

  2. Используйте Graphics and Textдля Ascii art: например, Star Programming! и построить аналоговые часы

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

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

Еще больше байтов сохраняется, если вектор длиннее двух нулей. Это также может использоваться для инициализации нулевых матриц, например, следующее дает матрицу 2x2 нулей:

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в списке есть как минимум три параметра:

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

7
+1; это просто говорит о том, что, хотя Mathematica может быть встроена во все, игра в гольф может быть настоящей проблемой.
LegionMammal978

Если вы хотите массив, который в конечном итоге заполнен 1 с, то 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 Quine (я знаю)

ToString[#0][] & []

Немного раздражает то, что он не даст вам точных символов, которые вы ввели. Например, если использовать @для приложения функции, он все равно будет отображаться как [...]и пробелы будут вставлены в некоторых местах. Обычно это приводит к тому, что квайн будет немного длиннее, чем хотелось бы, но он всегда будет работать, сначала играя в гольф, а затем просто копируя его вывод - который теперь должен быть настоящим квинем.

Помимо кавычек это также означает, что вы можете писать рекурсивный код без необходимости называть вашу функцию. Сравните эти три (наивные, но играющие в гольф) реализации Фибоначчи:

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]как есть. Тем не менее, это работает в большинстве случаев, в том числе, если uэто число, строка или список.

Кредиты для этого трюка идут на алефальфу - я обнаружил это в одном из его ответов.

Если 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]. TIO .
user202729

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

Только Mathematica 10

Операторские формы

Mathematica 10 поддерживает так называемые «операторные формы», что в основном означает, что некоторые функции могут быть карри. Каррирование функции - это создание новой функции путем фиксации одного из ее операторов. Скажем, вы используете SortBy[list, somereallylongfunction&]много разных listс. Раньше вы, вероятно, назначали SortByбы sи чистую функцию fтак

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

Композиция и Правильная Композиция

Есть новые сокращения для 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

Еще один байт может быть сохранен, если а) мы знаем, что элементы списка различны, и б) мы знаем нижнюю границу между 0 и 9 (включительно или верхнюю границу для обратного отсортированного порядка):

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

Чтобы понять, почему это работает, посмотрите «Последовательности аргументов» в подсказке, связанной сверху.


В качестве альтернативы, (строгое) Нижняя граница для обратной сортировки и работы: ##>0&@@a. Аналогично для верхней границы для отсортированного.
user202729

@ 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 Мин

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 до a n(обычно задается как ввод). По сути, есть 3 способа сделать это (используя в качестве примера безымянную идентификационную функцию):

#&/@Range@n
Array[#&,n]
Table[i,{i,n}]

Я склоняюсь к первому (по любой причине), но это редко лучший выбор.

С помощью Array вместо

Приведенный выше пример показывает, что using 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}

Если вы не используете инфиксную запись, 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логическим оператором.

Например, предположим, что вы хотите создать функцию, которая проверяет, является ли число простым, а print 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для второго и т. д. (см. этот ответ для более подробной информации).

Во многих случаях вы захотите вложить Functions. Например, 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]

2

Решить и уменьшить: автоматический список переменных

Когда список переменных для Solveи Reduceопущен, они решают для всех свободных переменных в выражении (ях). Примеры:

Solve[x^4==1]
(*    {{x -> -1}, {x -> -I}, {x -> I}, {x -> 1}}    *)
(*    two bytes saved on Solve[x^4==1,x]            *)

Solve[x^2+y^2==25,Integers]
(*    {{x -> -5, y -> 0}, {x -> -4, y -> -3}, {x -> -4, y -> 3}, {x -> -3, y -> -4},
       {x -> -3, y -> 4}, {x -> 0, y -> -5}, {x -> 0, y -> 5}, {x -> 3, y -> -4},
       {x -> 3, y -> 4}, {x -> 4, y -> -3}, {x -> 4, y -> 3}, {x -> 5, y -> 0}}    *)
(*    six bytes saved on Solve[x^2+y^2==25,{x,y},Integers]                         *)

Reduce[x^2+y^2<25,Reals]
(*    -5 < y < 5 && -Sqrt[25 - y^2] < x < Sqrt[25 - y^2]    *)
(*    six bytes saved on Reduce[x^2+y^2<25,{x,y},Reals]     *)

В частности , Solveнад Integers, NonNegativeIntegersи PositiveIntegersявляется чрезвычайно мощным для задач перечисления.


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.