How do I create a function that can take either positional or keyword arguments? Right now, I have a function that takes keyword arguments. But, I would to revise it to take either positional or keyword arguments. Thanks.
You can overload this function, e.g.:
julia> foo(x) = println(x)
foo (generic function with 2 methods)
julia> foo(;x=1) = foo(x)
foo (generic function with 2 methods)
julia> foo(42)
42
julia> foo(x=42)
42
Is it possible to do that without passing default values in optional arguments? The function I have is for a stats model and I do not want to pass default values.
Julia does dispatching on default values. So what you can do is:
function f(x,y=nothing;z=:SomeOption)
if y == nothing
return 5
else # y is some number
if z == :SomeOption
return x+2y
else
return x+3y
end
end
end
Via dispatch, it will compile a different function when y
is a number, so you donât need to worry about the internal checks. To see this, notice that when y
is not given, all of the other logic compiles away:
julia> @code_llvm f(2)
; Function Attrs: uwtable
define i64 @julia_f_68264(i64) #0 !dbg !5 {
top:
ret i64 5
}
and thisis just a function which returns 5. In the other case:
julia> @code_llvm f(2,3)
; Function Attrs: uwtable
define i64 @julia_f_68300(i64, i64) #0 !dbg !5 {
top:
%2 = call i8**** @jl_get_ptls_states() #4
%3 = alloca [7 x i8**], align 8
%.sub = getelementptr inbounds [7 x i8**], [7 x i8**]* %3, i64 0, i64 0
%4 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 2
%5 = bitcast i8*** %4 to i8*
call void @llvm.memset.p0i8.i32(i8* %5, i8 0, i32 40, i32 8, i1 false)
%6 = bitcast [7 x i8**]* %3 to i64*
store i64 10, i64* %6, align 8
%7 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 1
%8 = bitcast i8**** %2 to i64*
%9 = load i64, i64* %8, align 8
%10 = bitcast i8*** %7 to i64*
store i64 %9, i64* %10, align 8
store i8*** %.sub, i8**** %2, align 8
%11 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 6
%12 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 5
%13 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 4
%14 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 3
store i8** inttoptr (i64 2147434592 to i8**), i8*** %4, align 8
store i8** inttoptr (i64 71900760 to i8**), i8*** %14, align 8
store i8** inttoptr (i64 2147434584 to i8**), i8*** %13, align 8
%15 = call i8** @jl_box_int64(i64 signext %0)
store i8** %15, i8*** %12, align 8
%16 = call i8** @jl_box_int64(i64 signext %1)
store i8** %16, i8*** %11, align 8
%17 = call i8** @jl_invoke(i8** inttoptr (i64 2271350544 to i8**), i8*** %4, i32 5)
%18 = bitcast i8** %17 to i64*
%19 = load i64, i64* %18, align 16
%20 = load i64, i64* %10, align 8
store i64 %20, i64* %8, align 8
ret i64 %19
}
it compiles to a function which has to do a check on symbols and branch etc.
A better way, sometimes, is to use dispatch:
f(x;z=:SomeOption) = 5
function f(x,y;z=:SomeOption)
if z == :SomeOption
return x+2y
else
return x+3y
end
end
this is equivalent to the code above but may organize your code better in some cases. You can think of this as the âno default valuesâ case.
A very common way to use this is, if you have a lot of code that you want to share, have an internal function named _f
that does the big calculations. Then you have your different dispatches do setup and call _f
. This organizes by dispatch but still re-uses most of the internal code.
@ChrisRackauckas: Thanks. I thought it would be helpful to share part of the function I wrote which shows the parameter types as well. With using someOption works, but I can still declare the parameter types right?
For e.g.
function squared(;x=:SomeOption::Int)
Here is what my fuction looks like
function sAUC(; x::DataFrames.Formula = nothing, treatment_group::Symbol = nothing, data::DataFrames.DataFrame = nothing)
...
I would like to revise this function so it can take either all positional or keyword arguments. Thanks.
function squared(;x::Int=1)
But note that keyword arguments do not do dispatch yet, so this is actually a type assertion instead of normal dispatch. That should change before 1.0 is out:
@ChrisRackauckas: Can we make this function to take :SomeOption
in all parameters?
Thanks.
What is the reason for this? This R-coding style is not idiomatic in Julia.
I donât know what you mean. What are parameters? Thatâs not a Julian word for this, and we need to use specifics here since the details can be subtle.
@mkborregaard: Thanks. Do you mean that we can only do either positional or keyword arguments, but not both in Julia?
Yes, exactly (or to be exact, each argument should be either a keyword or a positional argument - you can combine both types in the same call). They are very different in Julia, as (at the moment) positional arguments are used to dispatch, keywords arenât.
@mkborregaard @ChrisRackauckas: I am deleting this response because it is still being tested.
@mkborregaard: you are correct about the following statement. Thanks. Is there any plan to allow both keyword and a positional argument in the future? Thanks a lot.
Yes, exactly (or to be exact, each argument should be either a keyword or a positional argument - you can combine both types in the same call).
I meant to say if we can use :SomeOption
for all parameters. It looks like we can. Thanks.
function squared(;x =:SomeOption::Int64, y =:SomeOption::Int64)
return x+y
end
@mkborregaard @ChrisRackauckas: Here is a workaround to the above problem. Any advice/comments? Thanks a lot.
function tryFunction(; x::Int64=nothing,y::Int64=nothing)
if x == nothing || y == nothing
error("Specify arguments")
end
tryFunction(x::Int64, y::Int64)
end
function tryFunction(x::Int64,y::Int64)
return(x + y)
end
tryFunction(x=2,y=7)
tryFunction(2,7)
tryFunction(y=2,x=7)
Since the argument defaults get evaluated at call time, you can actually have the default itself be an expression that throws an error:
julia> f(;x::Int=throw(ArgumentError("missing x"))) = x
f (generic function with 1 method)
julia> f(x=2)
2
julia> f()
ERROR: ArgumentError: missing x
in f() at ./REPL[1]:1
@mbauman:
@mkborregaard: Thanks. How do I combine this with the function so it takes positional arguments as well to fit into the tryFunction
I wrote above?
@mbauman: I tried to replicate the same function in Atom.But, it did not run. Any idea? Thanks.
f(; x::Int=throw(ArgumentError("missing x")))
return(x)
end
I got this error message;
syntax: keyword argument is not a symbol: "x::Int" in include_string(::String, ::String) at loading.jl:441 in include_string(::String, ::String, ::Int64) at eval.jl:28 in include_string(::Module, ::String, ::String, ::Int64, ::Vararg{Int64,N}) at eval.jl:32 in (::Atom.##53#56{String,Int64,String})() at eval.jl:40 in withpath(::Atom.##53#56{String,Int64,String}, ::Void) at utils.jl:30 in withpath(::Function, ::String) at eval.jl:46 in macro expansion at eval.jl:57 [inlined] in (::Atom.##52#55{Dict{String,Any}})() at task.jl:60
Youâre missing the function
(or to do like the example, where the function was defined in a short form using = x
).
This tryFunction
approach works, of course. There is nothing wrong with it I was just curious of why you wanted to achieve this design.
@mkborregaard: oh, my bad. So, that works without function in REPL , but in ATOM?? Thanks.