Possibility to sort conditional output?

First off, sorry if my title is bad, didn’t know what else to call it.

Say I have a function with inputs like these:

function readBi4Array(typ::Cat,SeekNull::Bool=false,Bi4Files::Array{String,1}=_dirFiles())

Where “typ” is always an enum named Cat, SeekNull is always a Bool and Bi4Files is always a string array. Then the following works:

k = readBi4Array(Idp,true)

But I am not able to do:

t =
3-element Array{String,1}:

k = readBi4Array(Idp,t)

I understand why, it expects a bool, but is there any easy way to make it so that it skips the bool and inserts “false” and then inserts t at array? I know I can do:

k = readBi4Array(Idp,false,t)

But I want to see if there is another option.

Kind regards

There are a couple of options - you could make Bi4Files a keyword argument for example, but I think the easiest thing to do is just define another method:

readBi4Array(typ::Cat,Bi4Files::Array{String,1}=_dirFiles()) = readBi4Array(typ, false, Bi4Files)

I think if you do this, you don’t actually need to set a default for SeekNull in the first method.

In fact, it might actually cause an ambiguity error if you leave the default value in the first method. I think I ran into something similar once and was confused for a long time…

Thanks for that one-liner, seems to solve it elegantly! And yes, if I try to leave it as default, I get a syntax error. Leaving it as false is great though, since the user then has to think and specifiy if he wants that parameter to be true.

Kind regards

Really? I think we must be thinking about different things then. And actually, I was wrong - what actually happens is that the second definition can overwrite the first in the ambiguous case:

julia> foo(a::String="foo", b::Int=1, c::Float64=42.) = "first method"
foo (generic function with 4 methods)

julia> foo(a::String="foo", c::Float64=42.) = "second method"
foo (generic function with 5 methods)

julia> foo("test")
"second method"

Hmmm, that was not my experience, maybe I did something wrong.

Will keep this in mind for the future, thanks for showcasing it.

Kind regards

I just want to make sure you realize why it was failing in the first place. Default values must be used sequentially because onec you use one, all the rest are default. This is because when you create this function is actually creates 3 methods:

function readBi4Array(typ::Cat)
function readBi4Array(typ::Cat,SeekNull::Bool)
function readBi4Array(typ::Cat,SeekNull::Bool,Bi4Files::Array{String,1})

So when you leave off Bi4Files, it calls the middle method, and when you leave off both it calls the first method. So one solution might be to swap the order of SeekNull and Bi4Files…basically you want the values most likely to be default last.

To expand on the solution by @kevbonham you might just want to do:

readBi4Array(typ::Cat,Bi4Files::Array{String,1}) = readBi4Array(typ, false, Bi4Files)

Which will then give you the methods:

function readBi4Array(typ::Cat)
function readBi4Array(typ::Cat,SeekNull::Bool)
function readBi4Array(typ::Cat,Bi4Files::Array{String,1})
function readBi4Array(typ::Cat,SeekNull::Bool,Bi4Files::Array{String,1})

And that new function will only be called when Type and Bi4Files is provided…(and no methods are overwritten).

EDIT: Wrong quote, meant to quote pixel27

It seems like your solution is exactly as earlier, but not with “=_dirFiles()” which is also quite important or did I misunderstand you?

Kind regards

You only need the default when calling the method with only the typ argument, and that’s covered by the first definition. In fact, if you do what I did, you’re actually overwriting the typ - only method you defined first.

EDIT: Expanding on my example:

ulia> foo(a::String="foo", b::Int=1, c::Float64=42.) = "first method"
foo (generic function with 4 methods)

julia> foo(a::String, c::Float64) = "second method"
foo (generic function with 5 methods)

julia> foo("test1")
"first method"

julia> foo(a::String, c::Float64=42.) = "third method"
foo (generic function with 5 methods)

julia> foo("test2")
"third method"

Okay let me try again. When you you declare:

function readBi4Array(typ::Cat,SeekNull::Bool=false,Bi4Files::Array{String,1}=_dirFiles())

Julia creates this automatically for you:

readBi4Array(typ::Cat) = readBi4Array(typ, false, _dirFiles())
readBi4Array(typ::Cat, SeekNull::Bool) = readBi4Array(typ, SeekNull, _dirFiles())

So when you also do:

readBi4Array(typ::Cat,Bi4Files::Array{String,1}=_dirFiles()) = readBi4Array(typ, false, Bi4Files)

Julia will create the following methods:

readBi4Array(typ::Cat) = readBi4Array(typ, _dirFiles())
readBi4Array(typ::Cat, Bi4Files::Array{String,1}) = readBi4Array(typ, false, Bi4Files)

You will notice that that first method readBi4Array(typ::Cat) just got defined again. First from your original function definition, then by your “bridge” definition. And the bridge definition has a kind of bounce, because it doesn’t call the ultimate function directly, first it calls the readBi4Array(::Cat, Array{String,1}) which then calls the correct function. (I’m sure the compiler optimizes this out, but…)

By not adding the default values in the second definition (What is it giving you anyway?)

Okay thanks @kevbonham and @pixel27, I am bit new to this, so it is still a bit hard for me to grasp, sorry, but I just checked and I see that using both suggestions I get four methods each time:

readBi4Array (generic function with 4 methods)

I ended up going with pixel’s suggestion and now all four cases work:


So it is slowly starting to make sense for me, so thanks again. I’ve got one more question though, which is that since some times I only want to read one file, ie. “Part_0000.bi4” it can be a bit annoying to also have to write the brackets. So I tried to do this:

readBi4Array(typ::Cat,Bi4Files::String) = readBi4Array(typ, false, Bi4Files)

And a 5th method is made, but it gives me the error:

ERROR: MethodError: no method matching readBi4Array(::Cat, ::Bool, ::String)
Closest candidates are:
  readBi4Array(::Cat, ::Bool, ::Array{String,1}) at 
  readBi4Array(::Cat, ::Bool) at 
  readBi4Array(::Cat, ::String) at 
 [1] readBi4Array(::Cat, ::String) at 
 [2] top-level scope at none:0

I am having a bit diffulty to see why that will not work, but maybe I am still not understanding it completely. Will keep trying.

Kind regards

It’s ok - it too me a while too :grinning: (and as you can see, I’m still not 100%).

Which method would you expect to be called by the right hand side of that new definition? The issue might be clearer if I change it slightly:

readBi4Array(typ::Cat,Bi4File::String) = readBi4Array(typ, false, Bi4File)

This means exactly the same thing as far as the language is concerned, but you’ll see that you haven’t defined the single-String case for having all 3 arguments. The simplest fix is just to do:

readBi4Array(typ::Cat,Bi4File::String) = readBi4Array(typ, false, [Bi4File])

Here, you make a 1-element array out of your single string and it calls the method you’ve previously defined.

That said, I find myself with this pattern a lot, and it might be more readable (and more maintainable in the long run) to change the way you’re thinking about it a bit. I would define the single-file method to be standalone, and then define the method (s) that acts on multiple files (calling the single-file method where appropriate). A trivial example:

function squareadd(num1::Int, num2::Int)
    return num1 + num2^2

function squareadd(num1::Int, nums::AbstractArray{Int,1})
    result = num1
    for num in nums
        result = squareadd(result, num) # this calls that first method I defined
    return result

(Note: I’m on mobile so I haven’t actually tested this code runs, but hopefully you get the idea)

Thanks got it to work now! And yes a very good suggestion, I will write this down in my notes, when I am going to rewrite the code at a later date (I hope) - just important to have something working for now. But the generality of Julia is very appealing and it was awesome to get a few explanations from you guys.

Kind regards

1 Like