What to include as function arguments

What things should be placed as function arguments? Do variables such as ranges need to be placed into the function argument or does this slow down/speed up Julia calculations?

I have two examples of identical functions but with different arguments and I would like to know which one is better to use. I thought one needed to include everything used in the function, but maybe this is not the case for stuff that is in the global scope?

aa = 4
bb = 5
cc = 6
qq = 3
ww = 1
ee = 10

function qwer(aa,bb,cc,qq,ww,ee)
    qwe = zeros(ee)
    qwe[ww:cc] .= aa+bb
    qwe[cc+1:end] .= qq
    return qwe
end

function qwer2(aa,bb,qq)
    qwerty = zeros(ee)
    qwerty[ww:cc] .= aa+bb
    qwerty[cc+1:end] .= qq
    return qwerty
end

fun = qwer(aa,bb,cc,qq,ww,ee)
fun2 = qwer2(aa,bb,qq)

You can use BenchmarkTools to test if it makes a difference:

julia> @btime qwer(aa,bb,cc,qq,ww,ee)

69.936 ns (1 allocation: 160 bytes)

julia> @btime qwer2(aa,bb,qq)

253.101 ns (9 allocations: 416 bytes)

As you can see the second one is slower and allocates more memory.

You can use @code_warntype qwer2(aa,bb,qq) to check the inferred return type of the function, in this case it’s Any, meaning the compiler doesn’t know anything about the type of the output, which is why it’s slower.

If you want to use global variables without performance penalty you need to declare them as const, which tells the compiler they won’t suddenly change. But passing arguments is preferable in most cases imo.

4 Likes

Note that at some point it makes sense to group external parameters in a struct which makes it much easier to pass them around. In this regard Parameters.jl can be handy.

using Parameters

@with_kw struct Params
  aa = 4
  bb = 5
  cc = 6
  qq = 3
  ww = 1
  ee = 10
end

p = Params()

function qwer(p)
  @unpack_Params p
  qwe = zeros(ee)
  qwe[ww:cc] .= aa+bb
  qwe[cc+1:end] .= qq
  return qwe
end

qwer(p)

Thank you for your reply.

Would it be correct to say that as a general rule, one should include all variables used in the function in the function arguments? Are there any exceptions? And does the order of arguments matter?

A function should generally be self-contained. That is it should not depend on any global state. Whether you assure that by passing parameters to the function as arguments or making them compiler constants by declaring them as const shouldn’t matter. The order of the function arguments doesn’t matter either.

6 Likes

I’d like to ask another question:

Does the order of the arguments in a function matter, when it is a function within a function?
I ran into some errors and a computer science colleague (works with Python and R) said the order must be the same. It fixed the error, so I would like more clarification.

function foo(a,s,d,f)
        something
        something2
end

function bar(q,w,e,r,a,s,d,f) # here I include a,s,d,f because it is needed in foo
    something3 *foo(a,s,d,f) 
# does the order of the arguments in foo 
# need to be the same as when the function is defined?
end

Julia allows both positional and keyword arguments. Unsurprisingly, the position of positional arguments matters, while keyword arguments can be in any order so long as they’re prescribed after the keyword arguments. You can also set a default value for positional arguments. See the docs for further details.

f(a, b=2; c=3, d=1) = a*b*c/d

f(1) # -> 6
f(1, c=30) # -> 60
f(1, 1, d=4, c=1) # -> 0.25
3 Likes