How to avoid code duplication when defining a function with multiple methods?

Hello! I am writing a long function which can take different numbers and types of arguments, so I am defining multiple methods to tackle each and every case. The function will behave similarly for every case, but there are some differences that stop me from creating just one general function. However, when I look at the script, I realise that every time you define a new method, you are generating a duplicate of the code, which should be avoided. My question: is there a way to define multiple methods for a function without having to duplicate the code?

It would greatly help if you could make this more concrete by posting code, ideally a MWE.

Thanks for replying. Here is a naive example of what I mean: as you define specific methods 2, 3 and 4 for the extra argument z and / or different type of y, your script gets longer and code gets duplicated.

#1 general method
function foo(x, y)
    x + y
end

#2 method to use when y is a vector (and not a scalar)
function foo(x, y::Vector{Number})
    x .+ y
end

#3 general method to use when z is given
function foo(x, y, z)
    x + y * z
end

#4 method to use when z is given and y is a vector (and not a scalar)
function foo(x, y::Vector{Number}, z)
    x .+ y .* z
end

In this specific example, you can get away with one definition:

function f(x, y, z=1)
    x + y * z
end

f(1, 1) == 1
f(1, 2, 3) == 7
f.(1, [1, 2]) == [2, 3]
f.(1, [1, 2], [3, 4]) == [4, 9])

In general you’ll want to separate out the part of your code that is not repeated into its own dispatched function, and call that from the main function.

So instead of

function f(x::Int, y::Int)
    println("This code is called every time.")
    println("Doing stuff to integer: ", x)
    println("Doing stuff to integer: ", y)
end

function f(x::Float64, y::Int)
    println("This code is called every time.")
    println("Doing stuff to float: ", x)
    println("Doing stuff to integer: ", y)
end

...

You do

function f(x, y)
    println("This code is called every time.")
    dostuff(x)
    dostuff(y)
end

dostuff(x::Int) = println("Doing stuff to integer: ",  x)
dostuff(x::Float64) = println ("Doing stuff to float: ", x)
...
3 Likes

Thanks for the insight! I managed to reduce the main function from 4 methods to just 1 that makes use of subfunctions with 2 or three methods. Also, for the changing number of arguments, I added a vararg foo(x, y, z...) instead of having two separate methods.