What's your favorite syntactical sugar in Julia

Primes.jl looks very suspicious. :thinking:

(primes(999).^2).% 24

3 Likes

that’s correct, it is a fact for all primes >= 5

Squared Primes Modulo 24
Roger B. Nelsen
Mathematics Magazine, 93 (2020) pg 228
DOI: 10.1080/0025570X.2020.1736877

10 Likes

Julia and Primes.jl turned many problems on https://projecteuler.net/ into jokes :joy:

10 Likes

Since Julia 1.6 I think

a = 1
b = 2
c = 3
d = 4

f(a, b; c, d) = a+b+c+d

f(a, b; c = c, d = d)

# no need to name them with `=` if variable name is same as argument name
f(a, b; c, d) 
12 Likes

That’s what I do with Emacs

2 Likes

When reduction operators take generators:

f(k) = sum(x^2 for x in 1:k)

even better than math notation!

14 Likes

seems much slower than

f(k) = sum((1:k).^2)

Unfortunately, this doesn’t work with sum, but if we define our own:

julia> function mysum(f, r)
           s = zero(f(first(r)))
           for i ∈ r
               s += f(i)
           end
           s
       end
mysum (generic function with 1 method)

julia> @btime mysum(abs2, 1:$(Ref(10))[])
  1.585 ns (0 allocations: 0 bytes)
385

julia> @btime mysum(abs2, 1:$(Ref(100))[])
  1.586 ns (0 allocations: 0 bytes)
338350

julia> @btime mysum(abs2, 1:$(Ref(1000))[])
  1.588 ns (0 allocations: 0 bytes)
333833500

julia> @btime mysum(abs2, 1:$(Ref(10000))[])
  1.586 ns (0 allocations: 0 bytes)
333383335000

LLVM can optimize away the loop, so performance is independent of k.

8 Likes

How so?

using BenchmarkTools, BenchmarkPlots, StatsPlots
K = 10_000
bg = BenchmarkGroup()
bg[:a] = @benchmark sum((1:$K).^2)
bg[:b] = @benchmark sum(x^2 for x in 1:$K)
bg[:c] = @benchmark sum(x -> x^2, 1:$K)
plot(bg; yscale=:log10)

image

The generator method is clearly much faster (but not as fast as the two argument version).

13 Likes

But still about 100x slower than mysum would be at that size.

It is indeed a lot faster even than sum(f, x). I wonder what it is about sum that can’t be optimized like mysum can.

I think it’s probably harder to constant fold pairwise summation.

1 Like

What about sum is pairwise? I would expect sum to do more or less the same thing as mysum

It’s a technique to increase floating-point accuracy : Pairwise summation - Wikipedia

9 Likes
 pkg> add BenchmarkPlots
    Updating registry at `C:\Users\Hermesr\.julia\registries\General`
    Updating git-repo `https://github.com/JuliaRegistries/General.git`
ERROR: The following package names could not be resolved:
 * BenchmarkPlots (not found in project, manifest or registry)

My bad, that’s an unmerged pr. You can clone it and add by directory if you want.

Another nice thing to do with end was given by @giordano in a Slack thread:

image

Alexander Palvin:
I wish it was also possible to store the indexing expression in a variable.

ix = min(i, end)
x[ix]  # same effect as x[min(i, end)]

@giordano , Using the package EndpointRanges.jl solved it using:

julia> using EndpointRanges

julia> using EndpointRanges: Endpoint, IndexFunction

julia> Base.min(x::Endpoint, y::Endpoint) = IndexFunction(r->Base.min(x(r), y(r)))

julia> Base.min(x::Endpoint, y::Number) = IndexFunction(r->Base.min(x(r), y))

julia> Base.min(x::Number, y::Endpoint) = IndexFunction(r->Base.min(x, y(r)))

julia> m = min(3, iend)
IndexFunction{var"#5#6"{Int64, EndpointRanges.IEnd}}(var"#5#6"{Int64, EndpointRanges.IEnd}(3, EndpointRanges.IEnd()))

julia> (1:10)[m]
3

julia> (1:2)[m]
2

@RoyiAvital that’s more an example of missing sugar though… It’s nice that it can be implemented but it would be nice to have something like this in Base.

2 Likes

@times it’s amazing

2 Likes

What package is that from?