Function that accepts scalars or one element vectors/ arrays

I would like to restrict a function to accept only argument values which contain only one number, how can this be achieved? My example below accept already scalars, vectors and arrays, but the argument value is not restricted to only one number, any ideas?

function _takeOneNumberInputsOnly(_oneNumber::Union{Vector{<:Number}, Array{<:Number}, Number})
    println("typeof(): ", typeof(_oneNumber), ", \t size(): ", size(_oneNumber))
end

# Example Inputs
oneElementVector = [1.2]
twoElementVector = [1, 2]
oneElementArray = ones(1,1)
scalar_value = 2.3

# This should be fine:
_takeOneNumberInputsOnly(oneElementVector)
_takeOneNumberInputsOnly(scalar_value)
_takeOneNumberInputsOnly(oneElementArray)

# This should throw an error:
_takeOneNumberInputsOnly(twoElementVector)

You cannot directly dispatch on the size of a (non static) array, thus probably the best is use an @assert.

Alternatively you could add an inner function witch dispatches on the Val of the length, but that’s probably too cumbersome.

Ps: Vector is an alias of Array, so the Union there is redundant. Also AbstractArray is probably better (accept views, etc)

2 Likes

Use the only function on your input arg.

function _takeOneNumberInputsOnly(_oneNumber)
    num = only(_oneNumber)
    println("typeof(): ", typeof(num), ", \t size(): ", size(num))
end

Works for all cases (I think.)

5 Likes

@assert statements could be disabled at various optimization levels, so it’s usually better to use throw(ArgumentError("message")).

1 Like

Yeah… I think that help entry must be improved. First it says:

Preferred syntax for writing assertions.

which does not suggest it is a debugging tool that should not be used at all in production code.

Then it mentions verifying passwords, which seems a pretty specific case, to end with nor should side effects needed for the function to work correctly be used inside of asserts.

which I really don’t understand what it means.

edit: added a pull request here trying to make it more clear: clarify assert doc entry by lmiq · Pull Request #45998 · JuliaLang/julia · GitHub

1 Like

Julia makes it easy for you, just use multiple dispatch as its best and more readable. From your question, only scalars and vectors/arrays are needed, so:

function _takeOneNumberInputsOnly(_oneNumber::Number)
    only(_oneNumber)
    # do whatever you want here
end
function _takeOneNumberInputsOnly(_oneNumber::Array{<:Number})
    only(_oneNumber)
    # do whatever you want here
end
_takOneNumberInputsOnly([12, 23])    # raises the error below

ArgumentError: Collection has multiple elements, must contain exactly 1 element

Stacktrace:
[1] only(x::Vector{Int64})
@ Base.Iterators ./iterators.jl:1358
[2] top-level scope
@ In[22]:1
[3] eval
@ ./boot.jl:373 [inlined]
[4] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1196

the only() function takes care of everything for you, so you don’t have to worry checking if the user entered more than one numbers.

one answer to consider is to not do this. instead, only define it for number and some call it with vectors. you are likely coming from Matlab which doesn’t give you good tools, but in Julia, it’s often worth thinking whenever you see a 1 element vector/matrix since it’s usually a sign of bad code.

7 Likes

Thanks DNF! - This works for me!

I have tried hard to write a minimalist sample code of the situation I am facing,
but fail :-(.
Part of my data is stored in a MAT-file (generated by Julia package MAT).
After reading back the vector eis_frequency_Shunt which is nested inside this MAT,
the result is of type Vector(Any). In the next step I take out one element of the
vector and the result is again a vector (Vector{Any}), holding one element.
Now it happens that the function that takes this one-element vector fails to execute,
because the compiler complains about a format mismatch (see below).
I had to use the notation [] to make it possible to run my script,
the annoying thing was, it took a while for me, to figure out the root-cause of the
crash.
Here a snippet of my code:

[...]
_frequ_Shunt  = eis_frequency_Shunt[indx_in_FFT_results]
_A, _B = MyLibCalibrationAmplPhase(_frequ_Shunt, B, C, D, E)

And here the definition of the function:

function MyLibCalibrationAmplPhase(_frequency::Real, _data_pts::Vector{<:Number},
 _sampl_rate::Real, _num_periods::Int=10, _LSQ_method::Int=0)
[...]
end

And here the error message (the first two lines are debugging output):

DBG: _frequ_Shunt,        type:  Vector{Any},   size: (1,)
DBG: eis_frequency_Shunt, type:  Vector{Any},   size: (46,)
[...]
ERROR: MethodError: no method matching MyLibCalibrationAmplPhase(::Vector{Any}, ::Vector{Float64}, ::Float64, ::Int64, ::Int64)
Closest candidates are:
  MyLibCalibrationAmplPhase(::Real, ::Vector{<:Number}, ::Real, ::Int64, ::Int64) at C:\data\git_repos\hycenta_julia\Julia_Modules\SignalAnalysis\HyCentaHarmonicSignalAnalysis.jl:148
  MyLibCalibrationAmplPhase(::Real, ::Vector{<:Number}, ::Real, ::Int64) at C:\data\git_repos\hycenta_julia\Julia_Modules\SignalAnalysis\HyCentaHarmonicSignalAnalysis.jl:148  
  MyLibCalibrationAmplPhase(::Real, ::Vector{<:Number}, ::Real) at C:\data\git_repos\hycenta_julia\Julia_Modules\SignalAnalysis\HyCentaHarmonicSignalAnalysis.jl:148

I can avoid this error by adding empty squared brackets: []:

 _frequ_Shunt    = eis_frequency_Shunt[indx_in_FFT_results][]

This was the trigger to think about, how to improve my function,
to handle this strange situation.