How to distinguish different objects in the same struct using Symbol in Julia? - struct

As mentioned in the title, I want to use multiple-dispatch to assign different behaviors of the same struct, distinguished by Symbol. The struct can be constructed as follows:
struct AbstractAlgorithm
algorithmName::String
algorithmSymbol::Symbol
function AbstractAlgorithm(algorithmName::String)
if algorithmName ∉ ("Value Iteration", "Policy Iteration")
error("Given algorithm $algorithmName not defined yet.")
elseif algorithmName=="Value Iteration"
new(algorithmName, Symbol("vIter"))
elseif algorithmName=="Policy Iteration"
new(algorithmName, Symbol("pIter"))
end
end
end
And I wanted to distinguish the same struct using different symbols in a function, such as:
function A(a::AbstractAlgorithm with Symbol vIter) = do A1
function A(a::AbstractAlgorithm with Symbol pIter) = do A2
How should I design function A using multiple-dispatch?

I am adding as an answer a small comment to what #Giovanni proposed since it is too long (+ some minor changes to your code).
If you want to use a parametric type (and not, as #DNF proposed a type hierarchy) you can just write:
struct Algorithm{T}
algorithmName::String
function Algorithm(algorithmName::AbstractString)
algorithmName == "Value Iteration" && return new{:vIter}(algorithmName)
algorithmName == "Policy Iteration" && return new{:pIter}(algorithmName)
error("Given algorithm $algorithmName not defined yet.")
end
end
function A(a::Algorithm{:pIter})
# ...
end
function A(a::Algorithm{:vIter})
# ...
end
The point is that Symbol can be used as type parameter, so you do not have to wrap it in Val.

This seems like a highly unusual and un-idiomatic approach, since Julia natively supports multiple dispatch. Also, AbstractNN is a naming convention for abstract types, while you are using it for a concrete type.
Why not instead use dispatch:
abstract type AbstractAlgorithm end
struct ValueIteration <: AbstractAlgorithm end
struct PolicyIteration <: AbstractAlgorithm end
and then implement your wanted behaviors based on the types of your algorithm objects:
A(::ValueIteration) = A1()
A(::PolicyIteration) = A2()

One solution could be using Val
struct AbstractAlgorithm{T}
algorithmName::String
function AbstractAlgorithm(algorithmName::String)
if algorithmName ∉ ("Value Iteration", "Policy Iteration")
error("Given algorithm $algorithmName not defined yet.")
elseif algorithmName=="Value Iteration"
new{Val{:vIter}}(algorithmName)
elseif algorithmName=="Policy Iteration"
new{Val{:pIter}}(algorithmName)
end
end
end
function A(a::AbstractAlgorithm{Val{:pIter}})
A1
end
function A(a::AbstractAlgorithm{Val{:vIter}})
A2
end

Related

Can I access a struct by name, eg A = field1, get struct.A?

Here's a psuedocode implementation of what I would be looking for within Julia:
struct Example
field1::Float64
field2::Float64
end # End struct
example = Example(1., 2.)
function modifystruct(mystruct, fieldname)
mystruct.fieldname +=10
return mystruct
end
modifystruct(example, field1)
# In this instance I would want to have example.field1 = 11.
How would I actually do this? I want to provide the fieldname as something like a string, and have my struct."whateverfieldname" get modified as such. I should add that I do NOT want to code something in like this:
function modifystruct(mystruct, fieldname)
if fieldname = "fieldname1"
mystruct.field1 +=10
end
if fieldname = "fieldname2"
mystruct.field2 +=10
end
return mystruct
end
Largely due to how versatile I want this code to be. I may be using different types of structs for my program, so the closest I can get to directly accessing by the name of the field, the better. Is there any method or implementation that can do this for me?
Sure, that's setproperty!(value, name, x) and getproperty(value, name):
function modifystruct(mystruct, fieldname)
new_field = getproperty(mystruct, fieldname) + 10
setproperty!(mystruct, fieldname, new_field)
return mystruct
end
As DecowVR rightly notes, this requires mystruct to be mutable.
If you want to do this repeatedly and with nested properties, you might be interested in lenses such as those provided by Setfield.jl.
Firstly, whould be noticed that in order to be able to modify an struct, it needs to be mutable:
julia> mutable struct Example
field1::Float64
field2::Float64
end
julia> example = Example(1., 2.)
Example(1.0, 2.0)
And now, a simple aproach would be to use Julia Symbols. A symbol is nothing else but an expression like :var. Can be used as shown:
julia> example.:field1
1.0
However, if we create a variable that stores the symbol, it won't work:
julia> v = :field1
:field1
julia> example.v
ERROR: type Example has no field v
Stacktrace:
[1] getproperty(x::Example, f::Symbol)
# Base ./Base.jl:42
[2] top-level scope
# REPL[18]:1
This is due to the order in which the Julia Interpreter works. If we want to evaluate firstly the variable, and then the expression, it is as easy as:
julia> #eval example.$v
1.0
So the complete function would be as follows:
julia> function modify_struct(mystruct::Example, fieldname::Symbol)
#eval $mystruct.$fieldname += 10
end

Struct equality with arrays

If I have arrays in a struct as below, I can't compare the equality of the struct because the arrays are mutable? Is there a way to get the equality to pass down to the array so that I get true for a([1,2,3]) == a([1,2,3])? Or is the only way to do this to extend Base.==?
julia> struct a
v
end
julia> a([1,2,3]) == a([1,2,3])
false
julia> a(1) == a(1)
true
julia> [1,2,3] == [1,2,3] # want the equality to work like this for the struct
true
julia> [1,2,3] === [1,2,3]
false
The answer by #miguel raz does not work at all!
This happens since isequal is actually calling == rather than == calling isequal. In the isequal doc you can find explicitely that:
The default implementation of isequal calls ==, so a type that does not involve
floating-point values generally only needs to define ==
Hence the correct code is:
struct A
v
end
import Base.==
==(x::A,y::A) = x.v==y.v
However, a more elegant approach would be to write a generic code that does not rely on having the field v. Since we do not want to overload the default == operator we can define an abstract type that will tell Julia to use our implementation:
abstract type Comparable end
import Base.==
function ==(a::T, b::T) where T <: Comparable
f = fieldnames(T)
getfield.(Ref(a),f) == getfield.(Ref(b),f)
end
Now you can define your own structures that will correctly compare:
struct B <: Comparable
x
y
end
Testing:
julia> b1 = B([1,2],[B(7,[1])]);
julia> b2 = B([1,2],[B(7,[1])])
julia> b1 == b2
true
As #Przemyslaw answered, an overload of == is needed.
For situations where an abstract class implementation doesn't work, the overload can be done as a one-liner (avoiding the import statement):
Base.:(==)(a::A, b::A) = Base.:(==)(a.v, a.v)
It's good to also override isequal (for structs that don't need special NaN and missing value semantics) and hash (for structs that can be used as keys):
Base.isequal(a::A, b::A) = Base.isequal(a.v, b.v)
Base.hash(a::A, h::UInt) = Base.hash(a.v, h)

julia: efficient and tidy way to avoid many function arguments

I have been writing stochastic PDE simulations in Julia, and as my problems have become more complicated, the number of independent parameters has increased. So what starts with,
myfun(N,M,dt,dx,a,b)
eventually becomes
myfun(N,M,dt,dx,a,b,c,d,e,f,g,h)
and it results in (1) messy code, (2) increased chance of error due to misplaced function arguments, (3) inability to generalise for use in other functions.
(3) is important, because I have made simple parallelisation of my code to evaluate many different runs of the PDEs. So I would like to convert my functions into a form:
myfun(args)
where args contains all the relevant arguments. The problem I am finding with Julia, is that creating a struct containing all my relevant parameters as attributes slows things down considerably. I think this is due to the continual accessing of the struct attributes. As a simple (ODE) working example,
function example_fun(N,dt,a,b)
V = zeros(N+1)
U = 0
z = randn(N+1)
for i=2:N+1
V[i] = V[i-1]*(1-dt)+U*dt
U = U*(1-dt/a)+b*sqrt(2*dt/a)*z[i]
end
return V
end
If I try to rewrite this as,
function example_fun2(args)
V = zeros(args.N+1)
U = 0
z = randn(args.N+1)
for i=2:args.N+1
V[i] = V[i-1]*(1-args.dt)+U*args.dt
U = U*(1-args.dt/args.a)+args.b*sqrt(2*args.dt/args.a)*z[i]
end
return V
end
Then while the function call looks elegant, it is cumbersome to rework accessing every attribute from the class and also this continual accessing of attributes slows the simulation down. What is a better solution? Is there a way to simply 'unpack' the attributes of a struct so they do not have to be continually accessed? And if so, how would this be generalised?
edit:
I am defining the struct I use as follows:
struct Args
N::Int64
dt::Float64
a::Float64
b::Float64
end
edit2: I have realised that structs with Array{} attributes can give rise to a performance difference if you do not specify the dimensions of the array in the struct definition. For example, if c is a one-dimensional array of parameters,
struct Args_1
N::Int64
c::Array{Float64}
end
will give far worse performance in f(args) than f(N,c). However, if we specify that c is a one-dimensional array in the struct definition,
struct Args_1
N::Int64
c::Array{Float64,1}
end
then the performance penalty disappears. This issue and the type instability shown in my function definitions seem to account for the performance difference I encountered when using a struct as the function argument.
In your code there is a type instability, related to U which is initialized as an 0 (integer), but if you replace it with 0. (floating point number), the type-instability disapears.
For the original versions (with "U=0"), function example_fun takes 801.933 ns (for the parameters 10,0.1,2.,3.) and example_fun2 925.323 ns (for similar values).
In the type-stable version (U=0.), both take 273 ns (+/5 ns). Thus this a substantial speed-up and there is no more a penalty of combining the arguments in the type args.
Here is the complete function:
function example_fun2(args)
V = zeros(args.N+1)
U = 0.
z = randn(args.N+1)
for i=2:args.N+1
V[i] = V[i-1]*(1-args.dt)+U*args.dt
U = U*(1-args.dt/args.a)+args.b*sqrt(2*args.dt/args.a)*z[i]
end
return V
end
Maybe you did not declare the types of the parameters of the type declaration of args?
Consider this small example:
struct argstype
N
dt
end
myfun(args) = args.N * args.dt
myfun is not type-stable can the type of the return type cannot be inferred:
#code_warntype myfun(argstype(10,0.1))
Variables:
#self# <optimized out>
args::argstype
Body:
begin
return ((Core.getfield)(args::argstype, :N)::Any * (Core.getfield)(args::argstype, :dt)::Any)::Any
end::Any
However, if you declare the types, then code becomes type-stable:
struct argstype2
N::Int
dt::Float64
end
#code_warntype myfun(argstype2(10,0.1))
Variables:
#self# <optimized out>
args::argstype2
Body:
begin
return (Base.mul_float)((Base.sitofp)(Float64, (Core.getfield)(args::argstype2, :N)::Int64)::Float64, (Core.getfield)(args::argstype2, :dt)::Float64)::Float64
end::Float64
You see that the inferred return type of Float64.
With parametric types (https://docs.julialang.org/en/v0.6.3/manual/types/#Parametric-Types-1), your code still remains generic and type-stable at the same time:
struct argstype3{T1,T2}
N::T1
dt::T2
end
#code_warntype myfun(argstype3(10,0.1))
Variables:
#self# <optimized out>
args::argstype3{Int64,Float64}
Body:
begin
return (Base.mul_float)((Base.sitofp)(Float64, (Core.getfield)(args::argstype3{Int64,Float64}, :N)::Int64)::Float64, (Core.getfield)(args::argstype3{Int64,Float64}, :dt)::Float64)::Float64
end::Float64

What (are there any) languages with only pass-by-reference?

I was wondering. Are there languages that use only pass-by-reference as their eval strategy?
I don't know what an "eval strategy" is, but Perl subroutine calls are pass-by-reference only.
sub change {
$_[0] = 10;
}
$x = 5;
change($x);
print $x; # prints "10"
change(0); # raises "Modification of a read-only value attempted" error
VB (pre .net), VBA & VBS default to ByRef although it can be overriden when calling/defining the sub or function.
FORTRAN does; well, preceding such concepts as pass-by-reference, one should probably say that it uses pass-by-address; a FORTRAN function like:
INTEGER FUNCTION MULTIPLY_TWO_INTS(A, B)
INTEGER A, B
MULTIPLY_BY_TWO_INTS = A * B
RETURN
will have a C-style prototype of:
extern int MULTIPLY_TWO_INTS(int *A, int *B);
and you could call it via something like:
int result, a = 1, b = 100;
result = MULTIPLY_TWO_INTS(&a, &b);
Another example are languages that do not know function arguments as such but use stacks. An example would be Forth and its derivatives, where a function can change the variable space (stack) in whichever way it wants, modifying existing elements as well as adding/removing elements. "prototype comments" in Forth usually look something like
(argument list -- return value list)
and that means the function takes/processes a certain, not necessarily constant, number of arguments and returns, again, not necessarily a constant, number of elements. I.e. you can have a function that takes a number N as argument and returns N elements - preallocating an array, if you so like.
How about Brainfuck?

Function Overloading and UDF in Excel VBA

I'm using Excel VBA to a write a UDF. I would like to overload my own UDF with a couple of different versions so that different arguments will call different functions.
As VBA doesn't seem to support this, could anyone suggest a good, non-messy way of achieving the same goal? Should I be using Optional arguments or is there a better way?
Declare your arguments as Optional Variants, then you can test to see if they're missing using IsMissing() or check their type using TypeName(), as shown in the following example:
Public Function Foo(Optional v As Variant) As Variant
If IsMissing(v) Then
Foo = "Missing argument"
ElseIf TypeName(v) = "String" Then
Foo = v & " plus one"
Else
Foo = v + 1
End If
End Function
This can be called from a worksheet as =FOO(), =FOO(number), or =FOO("string").
If you can distinguish by parameter count, then something like this would work:
Public Function Morph(ParamArray Args())
Select Case UBound(Args)
Case -1 '' nothing supplied
Morph = Morph_NoParams()
Case 0
Morph = Morph_One_Param(Args(0))
Case 1
Morph = Two_Param_Morph(Args(0), Args(1))
Case Else
Morph = CVErr(xlErrRef)
End Select
End Function
Private Function Morph_NoParams()
Morph_NoParams = "I'm parameterless"
End Function
Private Function Morph_One_Param(arg)
Morph_One_Param = "I has a parameter, it's " & arg
End Function
Private Function Two_Param_Morph(arg0, arg1)
Two_Param_Morph = "I is in 2-params and they is " & arg0 & "," & arg1
End Function
If the only way to distinguish the function is by types, then you're effectively going to have to do what C++ and other languages with overridden functions do, which is to call by signature. I'd suggest making the call look something like this:
Public Function MorphBySig(ParamArray args())
Dim sig As String
Dim idx As Long
Dim MorphInstance As MorphClass
For idx = LBound(args) To UBound(args)
sig = sig & TypeName(args(idx))
Next
Set MorphInstance = New MorphClass
MorphBySig = CallByName(MorphInstance, "Morph_" & sig, VbMethod, args)
End Function
and creating a class with a number of methods that match the signatures you expect. You'll probably need some error-handling though, and be warned that the types that are recognizable are limited: dates are TypeName Double, for example.
VBA is messy. I'm not sure there is an easy way to do fake overloads:
In the past I've either used lots of Optionals, or used varied functions. For instance
Foo_DescriptiveName1()
Foo_DescriptiveName2()
I'd say go with Optional arguments that have sensible defaults unless the argument list is going to get stupid, then create separate functions to call for your cases.
You mighta also want to consider using a variant data type for your arguments list and then figure out what's what type using the TypeOf statement, and then call the appropriate functions when you figure out what's what...

Resources