21
3
What general tips do you have for golfing in Julia? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Julia (e.g. "remove comments" is not an answer).
21
3
What general tips do you have for golfing in Julia? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Julia (e.g. "remove comments" is not an answer).
19
NOTE: The below may contain some outdated tips, as Julia is not quite stabilised in terms of structure, yet.
A few tricks to save a few characters
\ =div
, and you can then type a\b
instead of div(a,b)
. Note the space - this is necessary to avoid it parsing as a "\=" operator. Also note that, if overloaded at REPL prompt level, use (\)=Base.(\)
or \ =Base. \
to reset it. NOTE: some functions have existing UTF-8 operators pre-defined, such as ÷
for div
, as noted by Alex A.a>0?"Hi":""
, use "Hi"^(a>0)
to save one byte, or for boolean a, use "Hi"^a
to save three bytes.a=split("Hi there"," ")
, you may be able to avoid a[1]
and a[2]
by using a,b=split("Hi there"," ")
, which can be referenced as a
and b
, saving three bytes for each use, at the cost of just two extra characters at assignment. Obviously, do not do this if you can work with vector operations.[]
- for arrays, the expression A[]
is equivalent to A[1]
. Note that this does not work for Strings if you wish to get the first character, or for Tuples.==[]
for arrays and ==()
for tuples; similarly, for the negative, use !=[]
and !=()
. For strings, use ==""
for empty, but use >""
for not-empty, as "" is lexicographically before any other string.x<=1&&"Hi"
can be written as x>1||"Hi"
, saving a character (so long as the return of the boolean isn't important).in('^',s)
rather than contains(s,"^")
. If you can use other characters, you can save a bit more with '^'∈s
, but note that ∈
is 3 bytes in UTF-8.minimum(x)
or maximum(x)
, use min(x...)
or max(x...)
, to shave one character off your code, if you know x
will have at least two elements. Alternatively, if you know all elements of x
will be non-negative, use minabs(x)
or maxabs(x)
r"(?m)match^ this"
, type r"match^ this"m
, saving three characters.reverse(x)
is one byte longer than flipud(x)
and will perform the same operation, so the latter is better.{[1,2]}
, not {1,2}
) - for Julia 0.4, you would need Any[[1,2]]
.end
within array indexing, it automatically gets converted to the length of the array/string. So rather than k=length(A)
, use A[k=end]
to save 3 characters. Note that this may not be beneficial if you want to use k immediately. This also works in a multidimensional case - A[k=end,l=end]
will get the size of each dimension of A
- however, (k,l)=size(A)
is shorter by one byte in this case, so use it only if you want to immediately access the last element at the same time.A[k=1:end]
, in which case k
will hold an iterator matching 1:length(A)
. This can be useful when you want to also use array A
at the same time.collect(A)
, use [A...]
, which will do the same thing and save 4 bytes."$(s[i])"
or dec(s[i])
for expressions or multi-character variables, and "$i"
for single-character variables.?:
instead of &&
or ||
for conditional assignment - that is, if you want to perform an assignment only on some condition, you can save one byte by writing cond?A=B:1
rather than cond&&(A=B)
, or cond?1:A=B
rather than cond||(A=B)
. Note that the 1
, here, is a dummy value.union
or ∪
instead of unique
- union(s)
will do the same thing as unique(s)
, and save a byte in the process. If you can use non-ASCII characters, then ∪(s)
will do the same thing, and ∪
only costs 3 bytes instead of the 5 bytes in union
.15
Redefining operators can save a lot of bytes in parentheses and commas.
For a unary example, compare the following recursive implementations of the Fibonacci sequence:
F(n)=n>1?F(n-1)+F(n-2):n # 24 bytes
!n=n>1?!~-n+!(n-2):n # 20 bytes
!n=n>1?!~-n+!~-~-n:n # 20 bytes
The redefined operator retains its initial precedence.
Note that we could not simply swap out !
in favor of ~
, since ~
is already defined for integers, while !
is only defined for Booleans.
Even without recursion, redefining an operator is shorter than defining a binary function. Compare the following definitions of a simple divisibility test.
f(x,y)=x==0?y==0:y%x==0 # 23 bytes
(x,y)->x==0?y==0:y%x==0 # 23 bytes
x->y->x==0?y==0:y%x==0 # 22 bytes
x\y=x==0?y==0:y%x==0 # 20 bytes
The following illustrates how to redefine a binary operator to compute the Ackermann function:
A(m,n)=m>0?A(m-1,n<1||A(m,n-1)):n+1 # 35 bytes
^ =(m,n)->m>0?(m-1)^(n<1||m^~-n):n+1 # 36 bytes
| =(m,n)->m>0?m-1|(n<1||m|~-n):n+1 # 34 bytes
m\n=m>0?~-m\(n<1||m\~-n):n+1 # 28 bytes
Note that ^
is even longer than using a regular identifier, since its precedence is too high.
As mentioned before
m|n=m>0?m-1|(n<1||m|~-n):n+1 # 28 bytes
would not work for integer arguments, since |
is already defined in this case. The definition for integers can be changed with
m::Int|n::Int=m>0?m-1|(n<1||m|~-n):n+1 # 38 bytes
but that's prohibitively long. However, it does work if we pass a float as left argument and an integer as right argument.
11
Don't be too easily seduced by factor(n) The tempting base library function factor(n)
has a fatal flaw: it returns the factorization of your integer in an unordered Dict
type. Thus, it requires costly collect(keys())
and collect(values())
and potentially also a cat
and a sort
to get the data you wanted out of it. In many cases, it may be cheaper to just factor by trial division. Sad, but true.
Use map map
is a great alternative to looping. Be aware of the difference between map
and map!
and exploit the in-place functionality of the latter when you can.
Use mapreduce mapreduce
extends the functionality of map even further and can be a significant byte-saver.
Anonymous functions are great! ..especially when passed to the aforementioned map
functions.
Cumulative array functions cumprod
, cumsum
, the flavorful cummin
and the other similarly named functions enable cumulative operations along a specified dimension of an n-dimensional array. (Or *un*specified if the array is 1-d)
Short notation for Any When you want to select all of a particular dimension of a multi-dimensional array (or Dict), e.g. A[Any,2]
, you can save bytes by using A[:,2]
Use the single-line notation for functions Instead of function f(x) begin ... end
you can often simplify to f(x)=(...)
Use the ternary operator It can be a space-saver for single-expression If-Then-Else constructions. Caveats: While possible in some other languages, you cannot omit the portion after the colon in Julia. Also, the operator is expression-level in Julia, so you cannot use it for conditional execution of whole blocks of code.
if x<10 then true else false end
vs
x<10?true:false
1Might I suggest adjusting tip 3? mapreduce is longer than either mapfoldl or mapfoldr, and can have varying behaviour depending on implementation. mapfoldl and mapfoldr are left- and right-associative (respectively) consistently, and thus are a better choice. This also applies more generally to reduce (use foldl or foldr). – Glen O – 2015-07-15T09:07:55.630
factor is gone for the standard library – Lyndon White – 2017-07-08T03:17:26.257
3How on Earth is "use the ternary operator" somewhat specific to Julia? It's relevant to every language which has it. – Peter Taylor – 2014-03-17T17:40:19.633
5It's relevant that it has it. Many languages also have map, anonymous or pure functions, some kind of shorthand for any/all, cumulative functions, etc. If we were to reduce every tips thread to only the features absolutely unique to that language, there would be very little tips content. – Jonathan Van Matre – 2014-03-17T18:04:18.153
There are probably almost as many languages which don't have comments as there are languages which don't have the ternary operator. – Peter Taylor – 2014-03-17T18:24:02.687
3Gosh, only all the functional ones for starters, where everything returns a value so a ternary op would be redundant. If you must have specific examples: Go, Haskell, Scala, Lua, VB, Prolog, PL/SQL...even Python didn't for many versions. Of the near-dozen languages whose tips threads mention their ternary operator is there some reason you only chose to come be provincial in mine? – Jonathan Van Matre – 2014-03-17T18:50:23.390
I haven't seen the others. – Peter Taylor – 2014-03-17T19:01:53.993
3Well, hey, at least you're an equal-opportunity curmudgeon. ヘ( ̄ー ̄ヘ) – Jonathan Van Matre – 2014-03-17T19:14:37.123
9
This is also possible in other languages, but usually longer than the straightforward method. However, Julia's ability to redefine its unary and binary operators make it quite golfy.
For example, to generate the addition, subtraction, multiplication and division table for the natural numbers from 1 to 10, one could use
[x|y for x=1:10,y=1:10,| =(+,-,*,÷)]
which redefines the binary operator |
as +
, -
, *
and ÷
, then computes x|y
for each operation and x
and y
in the desired ranges.
This works for unary operators as well. For example, to compute complex numbers 1+2i, 3-4i, -5+6i and -7-8i, their negatives, their complex conjugates and their multiplicative inverses, one could use
[~x for~=(+,-,conj,inv),x=(1+2im,3-4im,-5+6im,-7-8im)]
which redefines the unary operator ~
as +
, -
, conj
and inv
, then computes ~x
for all desired complex numbers.
Female and Male Sequences (binary)
Case Permutation (unary)
6
return
f(x)=x+4
is identical to but shorter than f(x)=return x+4
. Julia always returns the result of the last statement.[x for x in 1:4]
is 3 characters longer than, but equivalent to [x for x=1:4]
6
Keywords can sometimes immediately follow constants without the need for a space or semicolon. For example:
n->(for i=1:n n-=1end;n)
Note the lack of a space between 1
and end
. This is also true for end
occurring after a close paren, i.e. )end
.
Perform integer division using ÷
rather than div()
or overloading an operator. Note that ÷
is worth 2 bytes in UTF-8.
Use vec()
or A[:]
(for some array A
) rather than reshape()
whenever possible.
Create functions rather than full programs when allowed in the challenge rules. It's shorter to define a function which accepts input rather than defining variables by reading from stdin. For example:
n->(n^2-1)
n=read(STDIN,Int);n^2-1
Variables can be incremented inside the argument to a function. For example, the following is my answer to the Find the Next 1-Sparse Binary Number challenge:
n->(while contains(bin(n+=1),"11")end;n)
This is shorter than incrementing n
inside of the loop.
5
Introduced in Julia 0.5. It is like map, but uses less characters, and does broadcasting behavour over it's args -- which means you can write less lambda's to deal with things.
Rather than:
map(f,x)
-- 8 characters.f.(x)
-- 5 charactersBetter still:
map(a->g(a,y),x)
-- 16 charactersg.(x,[y])
-- 9 characters
You can split on spaces using simply
split("Hi there")
since the pattern argument defaults to a space. – Alex A. – 2015-07-15T14:23:18.133@AlexA. - I know, but it's not the point of the tip, and the tip applies equally well either way. – Glen O – 2015-07-15T14:28:29.703
Point 12 has changed in 0.5. – Lyndon White – 2016-10-02T14:44:03.343
@Oxinabox - I'm not surprised, I'm pretty sure a few of them are outdated by now. I originally wrote most of the tips for 0.3, I think. – Glen O – 2016-10-02T16:19:51.030
?:
seems to be less useful in recent versions of Julia, as both need to be surrounded by spaces, adding 4 useless bytes. – ETHproductions – 2018-09-26T18:20:52.147@ETHproductions - yeah, there have been some changes that undoubtedly cause issues with some of the tips. I'll eventually update the tips to suit current behaviour. – Glen O – 2018-09-30T07:19:10.497
3Oh how I would love that first trick in Python. – seequ – 2014-06-04T18:04:48.603