define Julia functions with optional positional arguments - julia

From this link, it says:
# You can define functions with optional positional arguments
function defaults(a,b,x=5,y=6)
return "$a $b and $x $y"
end
defaults('h','g') # => "h g and 5 6"
defaults('h','g','j') # => "h g and j 6"
defaults('h','g','j','k') # => "h g and j k"
try
defaults('h') # => ERROR: no method defaults(Char,)
defaults() # => ERROR: no methods defaults()
catch e
println(e)
end
I think the purpose of this example is to show that, if the provided arguments are less than the default ones, the function will also return the default ones.
But why the error appears when one or no argument is provided? i.e. how do I know that providing two arguments are okay, but providing one or none is not okay?

The answer is quite simple:
For the function defaults it is required to give at least two arguments (a and b that have no default value), because the language specifications do not allow required positional arguments to be in an "unknown state". If no default value is provided, the interpreter raises the exception. Citing the manual, the design reason behind optional arguments:
Optional arguments are actually just a convenient syntax for writing multiple method definitions with different numbers of arguments
It is like they are creating some "functions" that already knows which are some of the arguments. You write:
defaults(a,b,x=5,y=6)
and internally exist:
defaults(a, b, x, y)
defaults(a, b, x, 6)
defaults(a, b, 5, 6)
(and notice that I'm not writing defaults(a, b, 5, y) to keep the discussion simple).
Calling defaults with 3 arguments is ok, but you will override the default value of x. Calling it with 4 arguments is ok, but you will override the default value of x and y. Calling it with less than 2 arguments is not ok, because the interpreter does not know what to return (since you need a and b to interpolate the returned string).
You may define a function that will require only one positional argument in this way:
function defaults(a,b=4,x=5,y=6)
return "$a $b and $x $y"
end
There are more information also here.

Related

Trying to pass an array into a function

I'm very new to Julia, and I'm trying to just pass an array of numbers into a function and count the number of zeros in it. I keep getting the error:
ERROR: UndefVarError: array not defined
I really don't understand what I am doing wrong, so I'm sorry if this seems like such an easy task that I can't do.
function number_of_zeros(lst::array[])
count = 0
for e in lst
if e == 0
count + 1
end
end
println(count)
end
lst = [0,1,2,3,0,4]
number_of_zeros(lst)
There are two issues with your function definition:
As noted in Shayan's answer and Dan's comment, the array type in Julia is called Array (capitalized) rather than array. To see:
julia> array
ERROR: UndefVarError: array not defined
julia> Array
Array
Empty square brackets are used to instantiate an array, and if preceded by a type, they specifically instantiate an array holding objects of that type:
julia> x = Int[]
Int64[]
julia> push!(x, 3); x
1-element Vector{Int64}:
3
julia> push!(x, "test"); x
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
Thus when you do Array[] you are actually instantiating an empty vector of Arrays:
julia> y = Array[]
Array[]
julia> push!(y, rand(2)); y
1-element Vector{Array}:
[0.10298669573927233, 0.04327245960128345]
Now it is important to note that there's a difference between a type and an object of a type, and if you want to restrict the types of input arguments to your functions, you want to do this by specifying the type that the function should accept, not an instance of this type. To see this, consider what would happen if you had fixed your array typo and passed an Array[] instead:
julia> f(x::Array[])
ERROR: TypeError: in typeassert, expected Type, got a value of type Vector{Array}
Here Julia complains that you have provided a value of the type Vector{Array} in the type annotation, when I should have provided a type.
More generally though, you should think about why you are adding any type restrictions to your functions. If you define a function without any input types, Julia will still compile a method instance specialised for the type of input provided when first call the function, and therefore generate (most of the time) machine code that is optimal with respect to the specific types passed.
That is, there is no difference between
number_of_zeros(lst::Vector{Int64})
and
number_of_zeros(lst)
in terms of runtime performance when the second definition is called with an argument of type Vector{Int64}. Some people still like type annotations as a form of error check, but you also need to consider that adding type annotations makes your methods less generic and will often restrict you from using them in combination with code other people have written. The most common example of this are Julia's excellent autodiff capabilities - they rely on running your code with dual numbers, which are a specific numerical type enabling automatic differentiation. If you strictly type your functions as suggested (Vector{Int}) you preclude your functions from being automatically differentiated in this way.
Finally just a note of caution about the Array type - Julia's array's can be multidimensional, which means that Array{Int} is not a concrete type:
julia> isconcretetype(Array{Int})
false
to make it concrete, the dimensionality of the array has to be provided:
julia> isconcretetype(Array{Int, 1})
true
First, it might be better to avoid variable names similar to function names. count is a built-in function of Julia. So if you want to use the count function in the number_of_zeros function, you will undoubtedly face a problem.
Second, consider returning the value instead of printing it (Although you didn't write the print function in the correct place).
Third, You can update the value by += not just a +!
Last but not least, Types in Julia are constantly introduced with the first capital letter! So we don't have an array standard type. It's an Array.
Here is the correction of your code.
function number_of_zeros(lst::Array{Int64})
counter = 0
for e in lst
if e == 0
counter += 1
end
end
return counter
end
lst = [0,1,2,3,0,4]
number_of_zeros(lst)
would result in 2.
Additional explanation
First, it might be better to avoid variable names similar to function names. count is a built-in function of Julia. So if you want to use the count function in the number_of_zeros function, you will undoubtedly face a problem.
Check this example:
function number_of_zeros(lst::Array{Int64})
count = 0
for e in lst
if e == 0
count += 1
end
end
return count, count(==(1), lst)
end
number_of_zeros(lst)
This code will lead to this error:
ERROR: MethodError: objects of type Int64 are not callable
Maybe you forgot to use an operator such as *, ^, %, / etc. ?
Stacktrace:
[1] number_of_zeros(lst::Vector{Int64})
# Main \t.jl:10
[2] top-level scope
# \t.jl:16
Because I overwrote the count variable on the count function! It's possible to avoid such problems by calling the function from its module:
function number_of_zeros(lst::Array{Int64})
count = 0
for e in lst
if e == 0
count += 1
end
end
return count, Base.count(==(1), lst)
The point is I used Base.count, then the compiler knows which count I mean by Base.count.

Is it possible to do multiple dispatch on NOT-a-type?

To simplify, I am trying to write a function with two arguments, where:
The base method accepts two Integers as arguments
func(x::Int, y::Int) = something
Additional methods accept either or both arguments as arbitrary types, map these arguments onto integers, and call the base method
Additional methods accept either or both arguments as arrays (or ::Colon types) and generate an array from applying the appropriate prior methods (1) or (2) elementwise.
Unsurprisingly (in hindsight), this approach generates method ambiguities. Given the types of arguments provide to a function, Julia chooses a valid method with the most specific types. But if x is an array and y is an Int, the following methods are equally specific and Julia doesn't know which one to call:
func(x::Any, y::Int)
func(x::Array, y::Any)
I would like to do something like
func(x::T, y::Int) T <: any_so_long_as_not_array = func(map_x_to_Int(x), y)
func(x::Array, y::Any) = (el -> func(el, y)).(x)
Is there such as thing as a not-a-type type? Am I thinking about this the wrong way? Is there a canonical way to approach this sort of problem?
For context, I am trying to implement a Base.getindex for a struct I wrote, and I want the getindex to support many different ways to index the struct when the contents of the struct could be somewhat diverse. Under-the-hood, elements in the struct are indexed by integers, but the user might be using almost-arbitrary non-integer types to index elements in the struct (I don't want to force the user to use particular types to index elements).
You can specify the (Array, Int) case and then add less specific methods:
julia> func(x::Array, i::Int) = 0
func (generic function with 1 method)
julia> func(x, i::Int) = 1
func (generic function with 2 methods)
julia> func(x::Array, i) = 2
func (generic function with 3 methods)
julia> methods(func)
# 3 methods for generic function "func":
[1] func(x::Array, i::Int64) in Main at REPL[1]:1
[2] func(x, i::Int64) in Main at REPL[2]:1
[3] func(x::Array, i) in Main at REPL[3]:1

How to pass FsCheck Test Correctly

let list p = if List.contains " " p || List.contains null p then false else true
I have such a function to check if the list is well formatted or not. The list shouldn't have an empty string and nulls. I don't get what I am missing since Check.Verbose list returns falsifiable output.
How should I approach the problem?
I think you don't quite understand FsCheck yet. When you do Check.Verbose someFunction, FsCheck generates a bunch of random input for your function, and fails if the function ever returns false. The idea is that the function you pass to Check.Verbose should be a property that will always be true no matter what the input is. For example, if you reverse a list twice then it should return the original list no matter what the original list was. This property is usually expressed as follows:
let revTwiceIsSameList (lst : int list) =
List.rev (List.rev lst) = lst
Check.Verbose revTwiceIsSameList // This will pass
Your function, on the other hand, is a good, useful function that checks whether a list is well-formed in your data model... but it's not a property in the sense that FsCheck uses the term (that is, a function that should always return true no matter what the input is). To make an FsCheck-style property, you want to write a function that looks generally like:
let verifyMyFunc (input : string list) =
if (input is well-formed) then // TODO: Figure out how to check that
myFunc input = true
else
myFunc input = false
Check.Verbose verifyMyFunc
(Note that I've named your function myFunc instead of list, because as a general rule, you should never name a function list. The name list is a data type (e.g., string list or int list), and if you name a function list, you'll just confuse yourself later on when the same name has two different meanings.)
Now, the problem here is: how do you write the "input is well-formed" part of my verifyMyFunc example? You can't just use your function to check it, because that would be testing your function against itself, which is not a useful test. (The test would essentially become "myFunc input = myFunc input", which would always return true even if your function had a bug in it — unless your function returned random input, of course). So you'd have to write another function to check if the input is well-formed, and here the problem is that the function you've written is the best, most correct way to check for well-formed input. If you wrote another function to check, it would boil down to not (List.contains "" || List.contains null) in the end, and again, you'd be essentially checking your function against itself.
In this specific case, I don't think FsCheck is the right tool for the job, because your function is so simple. Is this a homework assignment, where your instructor is requiring you to use FsCheck? Or are you trying to learn FsCheck on your own, and using this exercise to teach yourself FsCheck? If it's the former, then I'd suggest pointing your instructor to this question and see what he says about my answer. If it's the latter, then I'd suggest finding some slightly more complicated function to use to learn FsCheck. A useful function here would be one where you can find some property that should always be true, like in the List.rev example (reversing a list twice should restore the original list, so that's a useful property to test with). Or if you're having trouble finding an always-true property, at least find a function that you can implement in at least two different ways, so that you can use FsCheck to check that both implementations return the same result for any given input.
Adding to #rmunn's excellent answer:
if you wanted to test myFunc (yes I also renamed your list function) you could do it by creating some fixed cases that you already know the answer to, like:
let myFunc p = if List.contains " " p || List.contains null p then false else true
let tests =
testList "myFunc" [
testCase "empty list" <| fun()-> "empty" |> Expect.isTrue (myFunc [ ])
testCase "nonempty list" <| fun()-> "hi" |> Expect.isTrue (myFunc [ "hi" ])
testCase "null case" <| fun()-> "null" |> Expect.isFalse (myFunc [ null ])
testCase "empty string" <| fun()-> "\"\"" |> Expect.isFalse (myFunc [ "" ])
]
Tests.runTests config tests
Here I am using a testing library called Expecto.
If you run this you would see one of the tests fails:
Failed! myFunc/empty string:
"". Actual value was true but had expected it to be false.
because your original function has a bug; it checks for space " " instead of empty string "".
After you fix it all tests pass:
4 tests run in 00:00:00.0105346 for myFunc – 4 passed, 0 ignored, 0
failed, 0 errored. Success!
At this point you checked only 4 simple and obvious cases with zero or one element each. Many times functions fail when fed more complex data. The problem is how many more test cases can you add? The possibilities are literally infinite!
FsCheck
This is where FsCheck can help you. With FsCheck you can check for properties (or rules) that should always be true. It takes a little bit of creativity to think of good ones to test for and granted, sometimes it is not easy.
In your case we can test for concatenation. The rule would be like this:
If two lists are concatenated the result of MyFunc applied to the concatenation should be true if both lists are well formed and false if any of them is malformed.
You can express that as a function this way:
let myFuncConcatenation l1 l2 = myFunc (l1 # l2) = (myFunc l1 && myFunc l2)
l1 # l2 is the concatenation of both lists.
Now if you call FsCheck:
FsCheck.Verbose myFuncConcatenation
It tries a 100 different combinations trying to make it fail but in the end it gives you the Ok:
0:
["X"]
["^"; ""]
1:
["C"; ""; "M"]
[]
2:
[""; ""; ""]
[""; null; ""; ""]
3:
...
Ok, passed 100 tests.
This does not necessarily mean your function is correct, there still could be a failing combination that FsCheck did not try or it could be wrong in a different way. But it is a pretty good indication that it is correct in terms of the concatenation property.
Testing for the concatenation property with FsCheck actually allowed us to call myFunc 300 times with different values and prove that it did not crash or returned an unexpected value.
FsCheck does not replace case by case testing, it complements it:
Notice that if you had run FsCheck.Verbose myFuncConcatenation over the original function, which had a bug, it would still pass. The reason is the bug was independent of the concatenation property. This means that you should always have the case by case testing where you check the most important cases and you can complement that with FsCheck to test other situations.
Here are other properties you can check, these test the two false conditions independently:
let myFuncHasNulls l = if List.contains null l then myFunc l = false else true
let myFuncHasEmpty l = if List.contains "" l then myFunc l = false else true
Check.Quick myFuncHasNulls
Check.Quick myFuncHasEmpty
// Ok, passed 100 tests.
// Ok, passed 100 tests.

Julia pass optional parameter to inner function call

I have something like the following
function test(; testvar=nothing)
# only pass testvar to function if it has a useful value
inner_function(testvar != nothing ? testvar2=testvar : # leave it out)
end
# library function, outside my control, testvar2 can't be nothing
function inner_function(; testvar2=useful value)
# do something
end
I know I can use if/else statements within test() but inner_function has lots of parameters so I would prefer to avoid that from a code duplication standpoint. Is this possible?
Note: inner_function cannot have testvar2 = nothing, if testvar2 is passed it has to have a valid value.
As #elsuizo points out, multiple dispatch is one of the core julia features which allows you to perform such operations. Find bellow a quick example:
>>> inner_func(x) = true;
>>> inner_func(::Type{Void}) = false;
Note: it seems Nothing has been renamed to Void (as a warning prompts out when I try to use it).
inner_func has 2 method definitions, if Void is passed as a parameter, the function will just return false (change this behaviour by do nothing or whatever you want to do). Instead, if the function receives anything else than Void, it will just do something else (in this case, return true).
Your test wouldn't have to perform any check at all. Just pass the parameters to the inner_func, and it will decide what to do (which of the 2 methods of the function inner_func to call) depending on the parameter type.
An example:
>>> a = [1, 2, 3, Void, 5];
>>> filter(inner_func, a)
4-element Array{Any,1}:
1
2
3
5
For the numerical elements, the function calls the method of the function that returns true, and thus, the numerical elements are returned. For the Void element, the second method of the function is called, returning false, and thus, not returning such element.

Standard name for a function that modifies a function to ignore an argument

I'm using Python because it's generally easy to read, but this is not a Python-specific question.
Take the following Python function strip_argument:
def strip_argument(func_with_no_args):
return lambda unused: func_with_no_args()
In use, I can pass a no-argument function to strip_argument, and it will return a function that accepts one argument that is never used. For example:
# some API I want to use
def set_click_event_listener(listener):
"""Args:
listener: function which will be passed the view that was clicked.
"""
# ...implementation...
# my code
def my_click_listener():
# I don't care about the view, so I don't want to make that an arg.
print "some view was clicked"
set_click_event_listener(strip_argument(my_click_listener))
Is there a standard name for the function strip_argument? I'm interested in any languages that have a function like this in the standard library.
Most functional programming languages offer a const function, that's a function that will always ignore it's first parameter and return it's second. If you pass a function to const that's exactly the behavior you described.
In Haskell you can use it like that:
f x = x + 1
g = const f
g 2 3 == 4 --2 is ignored and 3 is incremented
I have done a quick search for such a function in python but haven't found anything. It seems the standard is to use a lambda function as you did.

Resources