In what way are Julia sets immutable? - set

If I create a set in Julia, then Julia will tell me that the set is immutable.
julia> pets = Set(["dog", "cat", "budgerigar"])
Set{String} with 3 elements:
"cat"
"budgerigar"
"dog"
julia> ismutable(pets)
false
Nonetheless, I can modify the set in place.
julia> push!(pets, "orangutan")
Set{String} with 4 elements:
"orangutan"
"cat"
"budgerigar"
"dog"
And I can check that the set contents have changed.
julia> display(pets)
Set{String} with 4 elements:
"orangutan"
"cat"
"budgerigar"
"dog"
Similarly, I can delete from the set in place
julia> delete!(pets, "dog")
Set{String} with 3 elements:
"orangutan"
"cat"
"budgerigar"
So my question is, in what way are sets immutable? In what way is their mutability different when compared with dictionaries?
julia> ismutable(Dict())
true
What am I not understanding?

If you check the source for Set, you can see that a Set is just a wrapper on a Dict{T,Nothing}, and when you add a new item, say x::T, to the Set, julia just creates a new entry in the Dict of x => nothing. That is, the items are stored in the keys of the Dict, and the values are not relevant so are set to nothing.
Clearly, a Dict needs to be mutable, as you observed in the question. The Set itself does not need to be mutable, since all the mutation is performed within the Dict that is wrapped by the Set. To see what I mean, we can mess around with the internals of a Set.
julia> s = Set(["a", "b"])
Set{String} with 2 elements:
"b"
"a"
julia> s.dict
Dict{String,Nothing} with 2 entries:
"b" => nothing
"a" => nothing
julia> push!(s, "c")
Set{String} with 3 elements:
"c"
"b"
"a"
julia> s.dict["d"] = nothing
julia> s.dict
Dict{String,Nothing} with 4 entries:
"c" => nothing
"b" => nothing
"a" => nothing
"d" => nothing
julia> s.dict = Dict("a new set"=>nothing)
ERROR: setfield! immutable struct of type Set cannot be changed
Stacktrace:
[1] setproperty!(::Set{String}, ::Symbol, ::Dict{String,Nothing}) at ./Base.jl:34
[2] top-level scope at REPL[14]:1
The key insight here is that I can play around with the mutability of s.dict as much as I want. But because Set is immutable, I can't replace s.dict with an entirely new Dict. That triggers the error you see in my session above.
If it isn't clear what it means for an immutable type to have mutable internals, I asked a similar question about this years ago on StackOverflow. It can be found here

Related

Confused about type declarations in Julia

I'm trying to define a struct NodeLocator
#with_kw mutable struct NodeLocator{T<:Int64}
length::T = 0
value::Vector{T} = [0]
maxtreelength::T = 0 end
And there are no problems with it.
However, I thought that my definition would be cleaner if I instead did
#with_kw mutable struct NodeLocator{T<:Union{Int64, Vector{Int64}}} # changed definition of T
length::T = 0
value::T = [0] # changed type declaration here
maxtreelength::T = 0 end
#since
julia> Vector{Int64} <: Union{Int64,Vector{Int64}}
true
But in this case I get an error when constructing objects of this type:
ERROR: MethodError: no method matching NodeLocator(::Int64,
::Vector{Int64}, ::Int64) Closest candidates are: NodeLocator(::T,
!Matched::T, ::T) where T<:Union{Int64, Vector{Int64}} at
C:\Users\ivica.julia\packages\Parameters\MK0O4\src\Parameters.jl:526
For context, their construction includes an elementwise addition of Int64s to NodeLocator.value, and I think that what's happening is that after the first step the type of that field gets locked at Int64 and cannot be changed to Vector{Int64}.
How can I parameterise the type declaration of NodeLocator.value without running into this error? - and does it make sense to do so? My main issue is computation time so performance is quite important for me.
It appears that you are using the #with_kw macro from the Parameters package, so let's start by loading that package:
julia> using Parameters
Now, copying your definition
julia> #with_kw mutable struct NodeLocator{T<:Union{Int64, Vector{Int64}}}
length::T = 0
value::T = [0] # changed type declaration here
maxtreelength::T = 0 end
NodeLocator
we can see that this works fine if we construct a struct with either all ints or all vectors
julia> NodeLocator(0,0,0)
NodeLocator{Int64}
length: Int64 0
value: Int64 0
maxtreelength: Int64 0
julia> NodeLocator([0],[0],[0])
NodeLocator{Vector{Int64}}
length: Array{Int64}((1,)) [0]
value: Array{Int64}((1,)) [0]
maxtreelength: Array{Int64}((1,)) [0]
but fails for mixtures, including the default you have specified (effectively, (0, [0], 0))
julia> NodeLocator([0],[0],0)
ERROR: MethodError: no method matching NodeLocator(::Vector{Int64}, ::Vector{Int64}, ::Int64)
Closest candidates are:
NodeLocator(::T, ::T, ::T) where T<:Union{Int64, Vector{Int64}} at ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:526
Stacktrace:
[1] top-level scope
# REPL[38]:1
julia> NodeLocator() # use defaults
ERROR: MethodError: no method matching NodeLocator(::Int64, ::Vector{Int64}, ::Int64)
Closest candidates are:
NodeLocator(::T, ::T, ::T) where T<:Union{Int64, Vector{Int64}} at ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:526
Stacktrace:
[1] NodeLocator(; length::Int64, value::Vector{Int64}, maxtreelength::Int64)
# Main ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:545
[2] NodeLocator()
# Main ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:545
[3] top-level scope
# REPL[39]:1
This is because as written, you have forced each field of the struct to be the same type T which is in turn a subtype of the union.
If you really want each type to be allowed to be a Union, you could instead write something along the lines of:
julia> #with_kw mutable struct NodeLocator{T<:Int64}
length::Union{T,Vector{T}} = 0
value::Union{T,Vector{T}} = [0] # changed type declaration here
maxtreelength::Union{T,Vector{T}} = 0 end
julia> NodeLocator() # use defaults
NodeLocator{Int64}
length: Int64 0
value: Array{Int64}((1,)) [0]
maxtreelength: Int64 0
However, unless you really need the flexibility for all three fields to be unions, your original definition is actually much preferable. Adding Unions where you don't need them can only slow you down.
One thing that would make the code cleaner and faster would be if you could use a non-mutable struct. This is often possible even when you might not realize it at first, since an Array within a non-mutable struct is still itself mutable, so you can swap out elements within that array without needing the whole struct to be mutable. But there are certainly cases where you do indeed need the mutability -- and if you do need that, go for it.
Finally, I should note that there is not necessarily much point in constraining the numeric type to be T<:Int64 unless that is really the only numeric type you'll ever need. You could easily make your code more general by instead writing something like T<:Integer or T<:Number, which should not generally have any adverse performance costs.

How do I check if a dictionary has a key in it in Julia?

Suppose I have a Dict object and a key value and I want to see if there's already an entry in the dictionary for that key? How do I do this?
There are a few ways to do this. Suppose this is your dictionary:
d = Dict(
"aardvark" => 1,
"bear" => 2,
"cat" => 3,
"dog" => 4,
)
If you have a key you can check for its presence using the haskey function:
julia> haskey(d, "cat")
true
julia> haskey(d, "zebra")
false
A slightly fancier way to check this is to check if the key is in the set of keys returned by calling keys(d):
julia> ks = keys(d)
Base.KeySet for a Dict{String,Int64} with 4 entries. Keys:
"aardvark"
"bear"
"cat"
"dog"
julia> "cat" in ks
true
julia> "zebra" in ks
false
Finally, it's fairly common that you want to get the value associated with a key if it is present in the dictionary. You can do that as a separate step by doing d[k] after checking that k is present in keys(d) but that involves an additional dictionary lookup. Instead, if there is some sentinel value that you know cannot be a value in your dictionary, such as nothing, then you can use the get function to look up the key with a default:
v = get(d, k, nothing)
if v !== nothing
# keys(d) contains k
end
If you know nothing about the kinds of values that d can map keys to, this is not a safe option since it could be the case that the pair k => nothing is present in d.
Ok not exactly an answer but still relevant to the discussion, something I learnt recently about julia dictionary accessor method that has made my life a lot easier.
val = get(dict, "key", nothing)
also has a version that creates the dictionary entry and sets it to the default value if it does not already exist
val = get!(dict, "key", nothing)
which does away with writing a lot of blocks like this
if !haskey(dict, "key")
dict["key"] = nothing
end
val = dict["key"]
so now instead of littering your code with key checks and dictionary value initialization blocks you can use get! instead.

Ruby: deleting object while looping over list with that object

So I have multiple lists to keep track of objects in a 2D game, but if these objects go off screen I want to remove these objects so they are no longer updated. What I have below works for me, but this doesn't work in other languages. Usually I have to make another "destroy list" that saves the objects I want to destroy and then loop again to remove them, because you can't remove an object from the list while iterating without some visible glitch.
Is Ruby just not showing any visible glitch while doing this or does Ruby's array work differently when removing multiple possible objects from a list while it's still iterating?
objects = []
objects.each{|o| o.withinBounds ? o.update : objects.delete(o)}
In Ruby you will actually find a glitch if you do what you are saying.
Try this:
objects = [1,2,3,4]
objects.each { |el| objects.delete(el) }
=> [2, 4]
You would expect the result to be an empty array, but is not. We are messing up with the elements of the arr and each gets confused, because the length of the array has changed. The each iterator looks something like this in pseudocode:
count = 0
while length(objects) > count
yield objects[count]
count + 1
end
So, in the example I shown above, the reason why we get [2, 4] can be explained on a step by step analysis on what objects.each { |el| objects.delete(el) } is doing:
We start with 4 (length of objects) > 0.
Number 1 is yielded, and deleted.
count = 1
3 (length of objects) > 1
Number 3 is yielded and deleted.
count = 2
2 (length of objects) is not bigger than count
We are done, so we have [2, 4]
There is a better way to do what you are trying, by using delete_if:
new_objects = objects.delete_if {|o| o.withinBounds }

How to access key-value tuples in forward/reverse order for Dict and SortedDict? - Julia

Given a dictionary:
> d = Dict{Int, Int}(1=>123, 2=>51, 4=>23)
Dict{Int64,Int64} with 3 entries:
4 => 23
2 => 51
1 => 123
I could access a dictionary's value by its key, e.g.:
> d[4]
23
Or I could looped through the key-value pairs as such:
> for i in d
println(i)
end
4=>23
2=>51
1=>123
I've tried accessing the key as the first element of the list or even i.key, but it doesn't seem to be the right syntax:
julia> for i in d
println(i.key)
end
ERROR: type Pair has no field key
in macro expansion; at ./REPL[22]:2 [inlined]
in anonymous at ./<missing>:?
julia> for i in d
println(i[0])
end
ERROR: BoundsError: attempt to access 4=>23
at index [0]
in getindex(::Pair{Int64,Int64}, ::Int64) at ./operators.jl:609
in macro expansion; at ./REPL[23]:2 [inlined]
in anonymous at ./<missing>:?
And then I remembered that Julia is not 0th index, so it should be:
> for i in d
println(i[1], ' ', i[2])
end
4 23
2 51
1 123
> for i in d
println(i[1], ' ', i[2])
end
4 23
2 51
1 123
In this case is the BoundsError somewhat like the Python's IndexError when an index of the list isn't found?
The other part of the question is on SortedDict, how do I access the last Nth element in the SortedDict?
I've tried using the index syntax and I retrieved the value but not the tuple of (key,value).
julia> import DataStructures: SortedDict
julia> sd = SortedDict(d)
DataStructures.SortedDict{Int64,Int64,Base.Order.ForwardOrdering} with 3 entries:
1 => 123
2 => 51
4 => 23
julia> sd[end]
23
Also, how can I sort the dictionary based on the value?
And, lastly, how to reverse the sorted dict?
I've tried using Base.Order.ReverseOrding but it threw a MethodError:
julia> sd = SortedDict{Base.Order.ReverseOrdering}(d)
ERROR: MethodError: Cannot `convert` an object of type Dict{Int64,Int64} to an object of type DataStructures.SortedDict{Base.Order.ReverseOrdering,D,Ord<:Base.Order.Ordering}
This may have arisen from a call to the constructor DataStructures.SortedDict{Base.Order.ReverseOrdering,D,Ord<:Base.Order.Ordering}(...),
since type constructors fall back to convert methods.
in DataStructures.SortedDict{Base.Order.ReverseOrdering,D,Ord<:Base.Order.Ordering}(::Dict{Int64,Int64}) at ./sysimg.jl:53
I am not a Python user, but the documentation for IndexError looks similar to the documentation for BoundsError. Note that you can always query current Julia documentation with ?, e.g.
?BoundsError
You can sort dictionary keys and values:
d = Dict{Int,Int}(1 => 2, 3 => 4)
k = sort(collect(keys(d)) ### collect() forms an array that you can sort
v = sort(collect(values(d))
but I cannot see why you would want to sort by value. Why not simply use the values as keys?
You can loop through keys or values easily:
for k in keys(d) ### or "for v in values(d)..."
println(k)
end
When using SortedDict, make sure that such an ordering makes sense. In this toy example the keys of d are integers, and they have a logical order with isless and isequal; see documentation here.
You can obtain the last entry of a SortedDict with last:
using DataStructures
D = SortedDict(d)
last(D) ### 3=>4
Use the Reverse module to flip the order:
using Reverse
D2 = SortedDict(d, Reverse)

Julia: Sorting a dict of types

I have a dict filled with Job types
A job has a name(string) and a score(int)
I managed to load the jobs into a Dict, and I want to sort them using the Sort method based on the jobs scores. However, when I sort the dict (call it jobs), it gives me a new vector of the sorted scores.
is there any way to sort the dict while preserving which job has its specific score?
jobs = Dict([(nurse, nurse.score), (construction, construction.score),
(programmer, programmer.score), (retail, retail.score)])
sort(collect(values(jobs)))
so if I have nurse with a score of 3, programmer with a score of 6, retail with a score of 0, and construction with a score of 4, I would want the output to be a dict (or something similar) that would contain:
programmer, 6
construction, 4
nurse, 3
retail, 0
or, even better, could I sort it by the values but get the output as a vector with just the jobs? then reference that vector later in my code?
this works in your specific case:
jobs = Dict("nurse"=>3, "construction"=>4, "programmer"=>6, "retail"=>0)
jobpairs = collect(jobs)
jobvalues = collect(values(jobs))
sind = sort(collect(values(jobs)), rev=true)
julia> sortedNames = [jobpairs[i] for i in indexin(sind, jobvalues)]
4-element Array{Any,1}:
"programmer"=>6
"construction"=>4
"nurse"=>3
"retail"=>0
if two keywords have the same value, we need do more work to deal with indices.
UPDATE:
as Matt suggested in the comment below, we should use sortperm rather than indexin which won't work if the dict has at least two keywords that have the same value.
jobs = Dict("nurse"=>3, "construction"=>4, "foo"=>3, "programmer"=>6, "retail"=>0)
jobpairs = collect(jobs)
jobvalues = collect(values(jobs))
sind = sortperm(collect(values(jobs)), rev=true)
julia> sortedNames = [jobpairs[i].first for i in sind]
5-element Array{Any,1}:
"programmer"
"construction"
"foo"
"nurse"
"retail"
Sorting algorithm with less code, but I don't know about the performance and you would not have a dict as result:
sort(collect(jobs),by=x->x[2],rev=true)
Currently I think the recommended way to do it is:
julia> using DataStructures
julia> jobs = Dict("nurse"=>3, "construction"=>4, "programmer"=>6, "retail"=>0)
Dict{String,Int64} with 4 entries:
"programmer" => 6
"retail" => 0
"construction" => 4
"nurse" => 3
julia> sort!(OrderedDict(jobs), byvalue=true, rev=true)
OrderedDict{String,Int64} with 4 entries:
"programmer" => 6
"construction" => 4
"nurse" => 3
"retail" => 0
In this way you get a dictionary as you wanted, but it is OrderedDict so it can be sorted as you see.

Resources