Unpack dict entries inside a function - julia

I want to unpack parameters that are stored in a dictionary. They should be available inside the local scope of a function afterwards. The name should be the same as the key which is a symbol.
macro unpack_dict()
code = :()
for (k,v) in dict
ex = :($k = $v)
code = quote
$code
$ex
end
end
return esc(code)
end
function assign_parameters(dict::Dict{Symbol, T}) where T<:Any
#unpack_dict
return a + b - c
end
dict = Dict(:a => 1,
:b => 5,
:c => 6)
assign_parameters(dict)
However, this code throws:
LoadError: UndefVarError: dict not defined
If I define the dictionary before the macro it works because the dictionary is defined.
Does someone has an idea how to solve this? Using eval() works but is evaluated in the global scope what I want to avoid.

If you want to unpack them then the best method is to simply unpack them directly:
function actual_fun(d)
a = d[:a]
b = d[:b]
c = d[:c]
a+b+c
end
This will be type stable, relatively fast and readable.
You could, for instance, do something like this (I present you two options to avoid direct assignment to a, b, and c variables):
called_fun(d) = helper(;d...)
helper(;kw...) = actual_fun(;values(kw)...)
actual_fun(;a,b,c, kw...) = a+b+c
function called_fun2(d::Dict{T,S}) where {T,S}
actual_fun(;NamedTuple{Tuple(keys(d)), NTuple{length(d), S}}(values(d))...)
end
and now you can write something like:
julia> d = Dict(:a=>1, :b=>2, :c=>3, :d=>4)
Dict{Symbol,Int64} with 4 entries:
:a => 1
:b => 2
:d => 4
:c => 3
julia> called_fun(d)
6
julia> called_fun2(d)
6
But I would not recommend it - it is not type stable and not very readable.
AFACT other possibilities will have similar shortcomings as during compile time Julia knows only types of variables not their values.
EDIT: You can do something like this:
function unpack_dict(dict)
ex = :()
for (k,v) in dict
ex = :($ex; $k = $v)
end
return :(myfun() = ($ex; a+b+c))
end
runner(d) = eval(unpack_dict(d))
and then run:
julia> d = Dict(:a=>1, :b=>2, :c=>3, :d=>4)
Dict{Symbol,Int64} with 4 entries:
:a => 1
:b => 2
:d => 4
:c => 3
julia> runner(d)
myfun (generic function with 1 method)
julia> myfun()
6
but again - I feel this is a bit messy.

In Julia 1.7, you can simply unpack named tuples into the local scope, and you can easily "spread" a dict into a named tuple.
julia> dict = Dict(:a => 1, :b => 5, :c => 6)
Dict{Symbol, Int64} with 3 entries:
:a => 1
:b => 5
:c => 6
julia> (; a, b, c) = (; sort(dict)...)
(a = 1, b = 5, c = 6)
julia> a, b, c
(1, 5, 6)
(The dict keys are sorted so that the named tuple produced is type-stable; if the keys were produced in arbitrary order then this would result in a named tuple with fields in arbitrary order as well.)

Related

How do I get the size of a dictionary in julia

How do I get the size of a Dictionary in julia? size() throws an error.
julia> d = Dict(:x => 1, :y => 2)
julia> size(d)
MethodError: no method matching size(::Dict{Symbol,Int64})
Use length().
julia> d = Dict(:x => 1, :y => 2)
julia> length(d)
2
The reason that size() doesn't work is that size is used to give the dimension of a container. From the docs:
size(A::AbstractArray, [dim])
Return a tuple containing the dimensions of A. Optionally you can specify a dimension to just get the length of that dimension.
and
length(A::AbstractArray)
Return the number of elements in the array, defaults to prod(size(A)).
The point with dictionaries is that they do not really have dimensions. You could of course represent them as one dimensional, but that would ignore the fact that the dictionary values can have "dimension", that are not necessarily uniform. For example, what dimensions should this dictionary then have? Two? Users might incorrectly assume they could access dict[:a][1]:
julia> dict = Dict(:a => 1, :b => [1, 2])
Dict{Symbol,Any} with 2 entries:
:a => 1
:b => [1, 2]

Vectorized indexing for a dictionary

I would like to find a succinct syntax in Julia for indexing a dictionary in a vectorized manner. In R, I would do the following:
dict <- c("a" = 1, "b" = 2)
keys <- c("a", "a", "b", "b", "a")
dict[keys]
In Julia, if I have a dict and keys like this,
dict = Dict(:a => 1, :b => 2)
keys = [:a, :a, :b, :b, :a]
then I can achieve the desired result using a list comprehension:
julia> [dict[key] for key in keys]
5-element Array{Int64,1}:
1
1
2
2
1
Is there a more succinct vectorized syntax, similar to the R syntax?
getindex.(Ref(dict), keys)
You can wrap it in Ref so you don't need to [] it.
Here's a little macro (using the brilliant MacroTools package):
using MacroTools
macro vget(ex)
#capture(ex, dict_.[idxvec_])
:(map(i->$dict[i], $idxvec)) |> esc
end
Then you can:
d = Dict(:a => 1, :b => 2)
ks = [:a, :a, :b, :b, :a]
#vget d.[ks]
I probably wouldn't want to use that in production code, something like your list comprehension, or a simple map: map(i->d[i], ks), is more readable/explicit/standard, but it is fun :D
If you're going to be frequently using the dictionary as a lookup table, then it might be worth creating a closure to use as a lookup function:
make_lookup(dict) = key -> dict[key]
dict = Dict(:a => 1, :b => 2)
lookup = make_lookup(dict)
Then you can use lookup in a vectorized fashion:
julia> keys = [:a, :a, :b, :b, :a];
julia> lookup.(keys)
5-element Array{Int64,1}:
1
1
2
2
1
You can use the vectorized version of getindex:
julia> getindex.([dict], keys)
5-element Array{Int64,1}:
1
1
2
2
1
Note that dict is wrapped in an array so that getindex does not attempt to broadcast over the elements of the dictionary:
julia> getindex.(dict, keys)
ERROR: ArgumentError: broadcasting over dictionaries and `NamedTuple`s is reserved
Stacktrace:
[1] broadcastable(::Dict{Symbol,Int64}) at ./broadcast.jl:615
[2] broadcasted(::Function, ::Dict{Symbol,Int64}, ::Array{Symbol,1}) at ./broadcast.jl:1164
[3] top-level scope at none:0

collect function keyword arguments in dict in julia

How can I get the keyword arguments and their default values of a function in a dict in Julia?
E.g:
function foo(x; a = 1, b = 2, c= 3)
# How do I get a dict of keyword arguments: Dict(a=>1, b=>2,c=3) ??,
# so I can pass this Dict easily to another generic function taking v
# variable keyword arguments for further processing
end
You can use the splat ... operator:
function foo(x; kwargs...)
Dict(kwargs)
end
or if you just want to pass it:
function foo(x; kwargs...)
innerfunction(x; kwargs...)
end
Just create a Dict like so:
function foo(x; a = 1, b = 2, c= 3)
Dict(:a => a, :b => b, :c => c)
end

Best way to eval in a given scope / context which is in the form of a Dict?

I have a string, e.g. z[2] and I want to eval it in a context, e.g. Dict(:z => 1:10)
What's the best way to do it?
I can make it sort of work, but it is very slow.
function replace_expr(expr, d::Dict)
return expr
end
function replace_expr(s::Symbol, d::Dict)
get(d, s, s)
end
function replace_expr(expr::Expr, d::Dict)
return Expr(replace_expr(expr.head, d),
[replace_expr(e, d) for e in expr.args]...)
end
function eval_with(context::Dict{Symbol, Any}, expr_string::AbstractString)
# E.g. :abc => :(s[:abc])
d = Dict(k => :(s[$(Meta.quot(k))]) for k in keys(context))
ex = parse("s -> $expr_string")
ex = replace_expr(ex, d)
return eval(ex)(context)
end
The following is the test
function make_context()
x = 1
y = "foo"
z = 2:5
Dict(
:x => x,
:y => y,
:z => z
)
end
const context = make_context()
#test eval_with(context, "x + 3") == 4
#test eval_with(context, "string(1, y, 1)") == "1foo1"
#test eval_with(context, "z[2]") == 3
#time eval_with(context, "z[2]")
# 0.004739 seconds (767 allocations: 40.728 KB)
This seems like a place where you can lean upon more of Julia's built-in expression evaluation machinery. eval takes an optional argument: the module in which the evaluation is to occur.
You can create new modules programmatically:
julia> M = Module()
anonymous
And you can assign values from a dictionary into that module with eval:
julia> context = Dict(
:x => 1,
:y => "foo",
:z => 2:5
);
julia> for (k,v) in context
eval(M, :($k = $v))
end
julia> M.x
1
julia> M.y
"foo"
And now, of course, you can evaluate your custom string within the context of your module.
julia> eval(M, parse("x+3"))
4
julia> eval(M, parse("string(1, y, 1)"))
"1foo1"
Dynamic evaluation like this is not going to be a place where Julia shines. I think this will be about as good as it gets:
julia> #time eval(M, parse("z[2]"))
0.000284 seconds (13 allocations: 672 bytes)
3
Note that this has slightly different semantics from the code you wrote above; the variables within your context only got populated at the beginning… and might be changed by a new evaluation.
And the usual caveats about using eval apply. There are often other, better ways of structuring your program that will be more performant, more understandable, and more maintainable.
If you know the values in advance, you can get around using eval via metaprogramming. A macro for this is provided by Parameters.jl:
d = Dict{Symbol,Any}(:a=>5.0,:b=>2,:c=>"Hi!")
#unpack a, c = d
a == 5.0 #true
c == "Hi!" #true

Extracting parameter types in Julia

Suppose I write a function in Julia that takes a Dict{K,V} as an argument, then creates arrays of type Array{K,1} and Array{V,1}. How can I extract the types K and V from the Dict object so that I can use them to create the arrays?
Sven and John's answers are both quite right. If you don't want to introduce method type parameters the way John's code does, you can use the eltype function:
julia> d = ["foo"=>1, "bar"=>2]
["foo"=>1,"bar"=>2]
julia> eltype(d)
(ASCIIString,Int64)
julia> eltype(d)[1]
ASCIIString (constructor with 1 method)
julia> eltype(d)[2]
Int64
julia> eltype(keys(d))
ASCIIString (constructor with 1 method)
julia> eltype(values(d))
Int64
As you can see, there are a few ways to skin this cat, but I think that eltype(keys(d)) and eltype(values(d)) are by far the clearest and since the keys and values functions just return immutable view objects, the compiler is clever enough that this doesn't actually create any objects.
If you're writing a function that will do this for you, you can make the types a parameter of the function, which may save you some run-time lookups:
julia> function foo{K, V}(d::Dict{K, V}, n::Integer = 0)
keyarray = Array(K, n)
valarray = Array(V, n)
# MAGIC HAPPENS
return keyarray, valarray
end
foo (generic function with 2 methods)
julia> x, y = foo(["a" => 2, "b" => 3])
([],[])
julia> typeof(x)
Array{ASCIIString,1}
julia> typeof(y)
Array{Int64,1}
You can use keys and values in combination with typeof:
# an example Dict{K, V}
d = Dict{Int64, ASCIIString}()
# K
typeof(keys(d))
Array{Int64,1}
# V
typeof(values(d))
Array{ASCIIString,1}
If you are just interested in the types, you can use eltype(d), or define even more specific functions
keytype{K}(d::Dict{K}) = K
valuetype{K,V}(d::Dict{K,V}) = V
and find the type immediately through
keytype(d)
valuetype(d)
As far as I understand, this should be pretty efficient because the compiler can deduce most of this at compile time.
For iterables, the eltype approach that is already given in other answers works well enough. E.g.
julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol, Int64} with 2 entries:
:a => 1
:b => 2
julia> K, V = eltype(keys(d)), eltype(values(d))
(Symbol, Int64)
julia> K
Symbol
julia> V
Int64
This approach is generalisable to all iterables. However I would like to offer another approach which will generalise to other kinds of object (and still doesn't use the method type signature approach):
julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol, Int64} with 2 entries:
:a => 1
:b => 2
julia> typeof(d)
Dict{Symbol, Int64}
julia> K, V = typeof(d).parameters
svec(Symbol, Int64)
julia> K
Symbol
julia> V
Int64
You can see that the Dict{Symbol, Int64} type has a field parameters which you can unpack to obtain the type parameters. This works on structs too:
julia> struct MyStruct{S, T}
a::S
b::T
end
julia> x = MyStruct(1, 1.0)
MyStruct{Int64, Float64}(1, 1.0)
julia> S, T = typeof(x).parameters
svec(Int64, Float64)
julia> S
Int64
julia> T
Float64

Resources