functionmethodsjuliakeyword-argumentmultiple-dispatch

How can I pass keyword arguments to the `range` function from within another function and maintain user flexibility with Julia?


I am writing my first module in Julia. I have a function f which will use a vector or range for some calculations. I would like to create a method of this function which will create a range using the range function before proceeding with the calculations in order to provide some flexibility to the user.

I have written the following:

# Attempt 1
function f(x,start,stop;length=1001,step=0.1)
    r=range(start,stop,length=length,step=step)
    # do more stuff with x and r
end
# error: length and step don't agree

However, range will accept only one of either step or length. It cannot take both unless they are defined in agreement. This leads me to want to define another function g which will be called inside of f. g would call range and have methods to account for the three possible scenarios.

  1. User specifies length when calling f.
  2. User specifies step when calling f.
  3. User specifies neither length nor step when calling f, so a default step value is used.

I'd rather not create more methods of f to avoid copying #do more stuff with x and r excessively. I would also like to avoid if statements where possible to take advantage of multiple dispatch and be efficient. Although, I haven't come up with any solutions so far.

I cannot define multiple methods of g with keyword arguments since the keyword arguments are optional.

# Attempt 2
function g(start,stop;length=1001)
    r=range(start,stop,length=length)
end
function g(start,stop;step=0.1)
    r=range(start,stop,step=step)
end
# error: the method definitions overlap

I also cannot convert the keyword arguments to regular arguments because I won't know which argument to pass.

# Attempt 3
function g(start,stop,length)
    r=range(start,stop,length=length)
end
function g(start,stop,step)
    r=range(start,stop,step=step)
end
function f(x,start,stop;length=1001,step=0.1)
    r=g(start,stop,y)
end
# error: no way to determine y or to differentiate length from step when passed to g

Solution

  • The range function uses nothing for step/length when they are not specified, so the following should work for you:

    function f(start, stop; step=nothing, length=nothing)
        if step === length === nothing
            step = 0.1 # default step
        end
        r = range(start, stop; step=step, length=length)
        return r
    end
    

    Examples:

    julia> f(1, 2; step=0.2)
    1.0:0.2:2.0
    
    julia> f(1, 2; length=3)
    1.0:0.5:2.0
    
    julia> f(1, 2) # default step
    1.0:0.1:2.0
    

    I'd rather not create more methods of f to avoid copying #do more stuff with x and r excessively

    A somewhat common pattern is to move the core functionality to another function, say _f, and have multiple entrypoints f which constructs the correct arguments for _f. Here is a sketch of that:

    function f(x, y)
        # construct arguments with x and y
        args = ...
        return _f(args...)
    end
    
    function f(x, y, z)
        # construct arguments with x, y and z
        args = ...
        return _f(args...)
    end
    
    function _f(args...)
        # core computation
    end