53
\$\begingroup\$

What general tips do you have for golfing in Mathematica? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Mathematica (e.g. "remove comments" is not an answer).

\$\endgroup\$

44 Answers 44

1
2
2
\$\begingroup\$

Default values

Default values deal with missing pattern arguments in an efficient way. For example, if we want to pattern match Exp[c_*x] in a rule for any value of c, the naïve

Exp[x] + Exp[2x] /. {Exp[c_*x] -> f[c], Exp[x] -> f[1]}
(*    f[1] + f[2]    *)

uses many more bytes than if we use the default value for c whenever it is missing:

Exp[x] + Exp[2 x] /. Exp[c_.*x] -> f[c]
(*    f[1] + f[2]    *)

The use of a default is indicated with a dot after the pattern: c_..

Default values are associated with operations: in the above example, the operation is Times in c_.*x, and a missing value for c_ is thus taken from the default value associated with Times, which is 1. For Plus, the default value is 0:

Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(*    f[0] + f[2]    *)

For Power exponents, the default is 1:

x + x^2 /. x^n_. -> p[n]
(*    p[1] + p[2]    *)
\$\endgroup\$
2
\$\begingroup\$

Solve and Reduce: automatic list of variables

When the list of variables for Solve and Reduce is omitted, they solve for all free variables in the expression(s). Examples:

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]     *)

Particularly Solve over the Integers, NonNegativeIntegers, and PositiveIntegers is extremely powerful for enumeration problems.

\$\endgroup\$
2
\$\begingroup\$

Flattening an Array

When using a multidimensional Array to compute a list of results that needs to be flattened, use a function with the attribute Flat as the fourth argument. The final result will be flattened with the specified head.

There are 23 such built-in functions at the time of this writing, 13 of which are also not Orderless. Some of the more generally applicable of those might be:

  • Or, if there are no booleans,
  • Dot, if there are no consecutive vectors with matching dimensions,
  • Join, if not all elements have the same head.

In addition, ##& will also flatten the array's elements; however, Sequences have special behavior when used in most functions, so the Array may often need to be wrapped in { }.

For comparison,

Array[f,dims,origin,Or]
{Array[f,dims,origin,##&]}
Join@@Array[f,dims,origin]  (*2d array only*)
Flatten@Array[f,dims,origin]

(* origin = 1 *)
Array[f,dims,1,Or]
{Array[f,dims,1,##&]}
Join@@Array[f,dims]         (*2d array only*)
Flatten[f~Array~dims]
\$\endgroup\$
3
  • 1
    \$\begingroup\$ If the head is not important, and the values are not Booleans, you can even use Or. Array[f,dims,1,Or] is one byte shorter than Join@@Array[f,dims]. \$\endgroup\$
    – alephalpha
    Nov 21, 2019 at 9:48
  • 1
    \$\begingroup\$ Here is an example: codegolf.stackexchange.com/a/119173/9288 \$\endgroup\$
    – alephalpha
    Nov 21, 2019 at 9:51
  • \$\begingroup\$ @alephalpha I completely overlooked Flat. Thanks! \$\endgroup\$
    – att
    Nov 25, 2019 at 4:15
2
\$\begingroup\$

Use Alphabet[] instead of FromLetterNumber

The function Alphabet was introduced in version 10.1, and it can reduce byte counts when you need to output lower-case/case-insensitive alphabetic strings.

FromLetterNumber@{8,5,12,12,15}<>"" (* 35 bytes *)
Alphabet[][[{8,5,12,12,15}]]<>""    (* 32 bytes *)
\$\endgroup\$
2
\$\begingroup\$

Use anonymous RuleDelayed (:>) instead of defining a single-argument function f@n_:=...

Sometimes an answer cannot be given as an anonymous function ...#...& but must be defined as a regular function f@n_:=... because of slot collisions. Such a definition, for example

f@n_:=Select[Range@n,#~CoprimeQ~n&]

can be shortened by two bytes to an anonymous delayed rule

n_:>Select[Range@n,#~CoprimeQ~n&]

that can be applied to a single argument.

\$\endgroup\$
3
  • \$\begingroup\$ Is this valid? I'd consider the expression to be scored here to be #/. pattern &. Also, \[Function] does the same thing while having lower precedence and the same byte count (counting the blank). \$\endgroup\$
    – att
    Oct 29, 2020 at 1:42
  • \$\begingroup\$ @att I don't see why the application of an anonymous function %[7] (which is usually not part of the scoring) should be treated differently from the application of an anonymous delayed rule 7/.%. \$\endgroup\$
    – Roman
    Oct 29, 2020 at 8:49
  • \$\begingroup\$ @att yes you're right about \[Function] (&#xF4A1;) having the same byte count. \$\endgroup\$
    – Roman
    Oct 29, 2020 at 8:53
2
\$\begingroup\$

Consider Set instead of ##&[] and ##2&

Set is equivalent to (#=##2)& - that is, Set[a, ...] is interpreted as a=Sequence[...]. This returns a Sequence of all arguments after the first. Try it online!

As a result, Set@a can be used like ##&[], but without the precedence issues.

a/.b_->Set@c
a/.b_->(##&[])
a/.b_->Nothing
\$\endgroup\$
2
\$\begingroup\$

Short forms for function-option combinations

In addition to its extensive list of operator short forms, like /@ for Map, Mathematica also has at least a couple of system-defined short forms for function-option combinations

  • Log[2, n]=> Log2@n
  • Log[10, n] => Log10@n

I don't know of any others, so feel free to either mention them in the comments, or edit this post to add more.

\$\endgroup\$
2
  • \$\begingroup\$ /@ for Map? \$\endgroup\$
    – att
    Jul 5, 2021 at 21:47
  • \$\begingroup\$ @att Thanks, I corrected the typo. \$\endgroup\$
    – theorist
    Jul 5, 2021 at 21:54
2
\$\begingroup\$

Use vector inequalities

Vector inequalities VectorLess, VectorLessEqual, VectorGreater, and VectorGreaterEqual were introduced in 12.0.

Importantly for golfing, they all have 3-byte private-use characters assigned, making them more efficient than, say, threading a comparison or using a difference.
Their respective characters are:

Note that these operators look similar to, but are distinct from the previously-existing \[Precedes], \[PrecedesEqual], \[Succeeds], \[SucceedsEqual].


Like scalar inequalities, the vector inequalities can take any number of arguments and can be chained (into a Developer`VectorInequality). They also thread over scalar arguments, including being able to take only scalar inputs. For the most part, they can be thought of as scalar inequalities that thread over lists and return results with And applied.

Some edge cases: they return unevaluated if there are non-list non-numeric elements (including e.g. \$\{0,a\}\succ\{1,0\}\$), and as far as I can tell, they always return False when the inputs are of different (non-scalar) dimensions.

As a small caveat, the calling syntax is different from that of regular inequalities: \$a\prec b\$ is VectorLess[{a, b}], not VectorLess[a, b].

\$\endgroup\$
2
\$\begingroup\$

Change the head when pattern matching subsequences

When pattern matching and replacing a short subsequence in a long list, we usually do something like:

{x___,a_,b_,y___}/;someCond:>{x,someExpr,y}

Sometimes we can change the head before pattern matching to something with the attribute Flat, like Or or Dot.

For example, this simple bubble sort (38 bytes)

#//.{x___,a_,b_,y___}/;a>b:>{x,b,a,y}&

can be written as (34 bytes)

List@@(Or@@#//.a_||b_/;a>b:>b||a)&

or (also 34 bytes)

List@@(Dot@@#//.a_ .b_/;a>b:>b.a)&

Don't forget to add a space between _ and . when you are using Dot, otherwise Mathematica would parse it as an Optional pattern.

But this only works when the input and output both have at least 2 elements.

\$\endgroup\$
2
\$\begingroup\$

Finding the product of a list post-processing

A typical golfy way to find the product of a list of values is by applying 1##&:

1##&@@expr

Sometimes, we may need to apply a pure function on all of the values first. In such cases, it's often shorter to wrap the result of that preprocessing in a List and use Dot instead:

Product[bodi,{i,expr}]
1##&@@(body&/@expr)
{body}&/@Dot@@expr

If the list has known arguments:

1##&@@(body&/@{a,b,c})
{body}&/@Dot[a,b,c]
{body}&/@(a.b.c)

(g=body&)[a]g[b]g@c
#@a#@b#@c&[body&]
\$\endgroup\$
2
\$\begingroup\$

Localizing temporary expressions

Some function submissions may involve defining a local expression which should not persist between different calls. For example, this answer uses expressions with head g to build paths, (re-)defining g[#] for each value in the input list. These paths should be reset on each new input.

The canonical way to do this is with Module[{g},...]/Block[{g},...], control structures which localize variables so that definitions made inside them will not propagate outside. For golfing, most of the time we can save a few bytes over these by using Clear@g instead, though it may not play nicely with recursion.

In some cases, we can do even better by setting g=h@# (or g=h@##). However, as subsequent definitions will not be cleared on repeated function calls, care needs to be taken to ensure that the function handles such repeated calls on the same input properly (for example, this answer would fail to do so). Where applicable, though, it not only saves bytes, but also has no issues with recursion.

Comparing these strategies for the linked example:

{g}~Module~Depth[i=0;(g@i=g[++i+#]!)&/@#]-3&
{g}~Block~Depth[i=0;(g@i=g[++i+#]!)&/@#]-3&
Depth[i=0Clear@g;(g@i=g[++i+#]!)&/@#]-3&
Depth[g=h@#;i=0;(g@i=g[++i+#]!)&/@#]-3&

When using two or more such local expressions, Block or Clear are the best bet:

Module[{a,b},...]
Block[{a,b},...]
(a~Clear~b;...)
(a=p@#;b=q@#;...)
\$\endgroup\$
2
\$\begingroup\$

PreIncrement/PreDecrement can save parentheses

When called on a non-atomic, non-list argument, PreIncrement/PreDecrement return the incremented/decremented value, albeit with an error message. This can save bytes on increments/decrements that would otherwise require parentheses.

For example, to generate the golden ratio:

(√5+1)/2
√5/2+1/2
++√5/2

Try it online!

Prepending + to an atom or list allows ++/-- to be used on them as well, thanks to the HoldFirst attribute. - works as well if the target is not a literal number. In my experience, this is most often useful when used with #. Try it online!

This is also only possible because, for whatever reason, +++x is interpreted as ++ +x instead of + ++x.

\$\endgroup\$
1
\$\begingroup\$

Golfing nested pure functions

This is my first post here, would be grateful for comments and corrections!

Let there is some pure function as criterion in Select, MaximalBy etc.:

Select[list, crit[#]&]

And inside this criterion we need some calculations with Map, Table etc which may also be presented as pure function:

crit[arr_, x_] := Table[f[x, e], {e, arr}]  

eq to

crit[arr_, x_] := f[x, #]&/@arr

Sometimes the whole construction may be effectively golfed with Fold, Outer etc.
But sometimes not, and we have troubles with correctly using slots in nested pure functions:

Select[ list, (f[#!!wrong!!, #]&/@arr)& ]

Possible golfing solution (without Table or Map) may be like this (thanks to @att):

Select[list, (x=#;f[x,#]&/@arr)&]

or

Select[list, x|->f[x,#]&/@arr]

or sometimes

(f[x,#]&/@arr)/.x->#&
\$\endgroup\$
1
  • 2
    \$\begingroup\$ usually better (x=#;f[x,#]&/@arr)& or x|->f[x,#]&/@arr \$\endgroup\$
    – att
    Apr 28, 2023 at 20:54
1
\$\begingroup\$

Check for duplicates

UnsameQ@@ is 6 bytes shorter than DuplicateFreeQ@.
But if performance matters, UnsameQ is over 1000 times slower.

\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.