Difference between Dict and Base.Iterators.Pairs in Julia - dictionary

In Julia v1.0 when using keyword arguments the resulting structure in the function will have the type Base.Iterators.Pairs.
julia> foo(;kwargs...) = println(kwargs)
julia> foo(a = 1, b = 2)
Base.Iterators.Pairs(:a=>1,:b=>2)
What is the difference between Iterators.Pairs and usual Dictionary? Why do we use this specific type?

In Julia 0.7/1.0, keyword arguments were changed to be stored as named tuples instead of dictionaries. The Pairs type is just a wrapper so that iterating gives you key,value pairs (iterating over a named tuple just gives values, like iterating over a tuple).

Related

Pass vector as command line argument in Julia

Is there a way to pass a vector variable as a command line argument in Julia? In my case I have two arguments: an integer and a vector of integers. While I can easily parse the first argument, I didn't find any pleasant way to parse a vector. For now I simply set the vector to be v = parse.(Int, ARGS[2:end]) but it is quite confusing since the items of the vector are treated as arguments. Is there a some special syntax to treat such cases?
I think your current solution is fine as it is, and in line with the way many command-line tools do things.
If you really want to pass your whole array as one command-line argument, you'll have to:
somehow make sure it is correctly parsed as one argument by the shell
parse it as an array within Julia
Both steps vary depending on the syntax you want to use.
Two examples:
example 1: Julia-like syntax
shell$ julia myscript.jl 42 "[1,2,3]"
i = 42
v = [1, 2, 3]
We can take advantage of the Julia parser being able to parse such arrays (but let's be cautious about not evaluating arbitrary julia code input by the user):
# First argument: an integer
i = parse(Int, ARGS[1])
# Second argument, a Vector{Int} in Julia-like format: "[1, 2, 3]"
v = let expr = Meta.parse(ARGS[2])
#assert expr.head == :vect
Int.(expr.args)
end
#show i
#show v
Example 2: space- or comma-separated values
shell$ julia myscript.jl 42 "1,2,3"
i = 42
v = [1, 2, 3]
Here, we can use DelimitedFiles to parse the array (change the delimiter to whatever you like):
# First argument: an integer
i = parse(Int, ARGS[1])
# Second argument, a Vector{Int} as comma-separated values
using DelimitedFiles
v = reshape(readdlm(IOBuffer(ARGS[2]), ',', Int), :)
# set the delimiter here ^
#show i
#show v

In-place modification/reassignment of vector in Julia without getting copies

Here's some toy code:
type MyType
x::Int
end
vec = [MyType(1), MyType(2), MyType(3), MyType(4)]
ids = [2, 1, 3, 1]
vec = vec[ids]
julia> vec
4-element Array{MyType,1}:
MyType(2)
MyType(1)
MyType(3)
MyType(1)
That looks fine, except for this behavior:
julia> vec[2].x = 60
60
julia> vec
4-element Array{MyType,1}:
MyType(2)
MyType(60)
MyType(3)
MyType(60)
I want to be able to rearrange the contents of a vector, with the possibility that I eliminate some values and duplicate others. But when I duplicate values, I don't want this copy behavior. Is there an "elegant" way to do this? Something like this works, but yeesh:
vec = [deepcopy(vec[ids[i]]) for i in 1:4]
The issue is that you're creating mutable types, and your vector therefore contains references to the instantiated data - so when you create a vector based on ids, you're creating what amounts to a vector of pointers to the structures. This further means that the elements in the vector with the same id are actually pointers to the same object.
There's no good way to do this without ensuring that your references are different. That either means 1) immutable types, which means you can't reassign x, or 2) copy/deepcopy.

Julia: Making empty/initialized multidimensional arrays of self defined types

I am making a type of my own called KeyVal defined as below:
type KeyVal
first::Int
second::Float64
end
And I am trying to make an empty/or initialized at zero matrix that its elements are of type KeyVal.
Normally with other types I do
myMat = zeros(KeyVal, (10,3))
But this will not work, because there is no zeros defined for this composite type. So I try to define my own zeros function in the following way:
import Base.zeros
function zeros(KeyVal,dims)
if length(dims) > 1
n=dims[1]
m=dims[2]
temp = repeat([KeyVal(0,0.0)], outer=m*n)
temp = reshape(temp, (n,m))
return temp
elseif length(dims) == 1
n=dims[1]
temp= repeat([KeyVal(0,0.0)], outer=n)
temp = reshape(temp, (n))
return temp
end
end
This adds to the methods list of other previously defined zeros.
But using it will generate errors:
myMat = zeros(KeyVal, (N,M))
MethodError: no method matching zero(::Type{KeyVal})
Closest candidates are:
.....
I am wondering whether I can resolve this in a way or maybe signal this in the type constructor so that any data structure that involves the type KeyVal is initialized at (first = 0, second=0.0).
Previously I tried defining the matrix as :
myMat2 = Array{KeyVal,(N,M)}
This will create the matrix except all its elements are #undef, and in this case I cannot access any of the elements of the myMat2:
myMat2[1,1]
UndefRefError: access to undefined reference
You can create an uninitialized array of any type T and dimension N with
Array{T, N}(undef, dims...)
In the special case of one and two dimensional arrays, you can use the aliases Vector and Matrix, e.g.,
julia> m = Matrix{KeyVal}(undef, 2,2)
2×2 Array{KeyVal,2}:
#undef #undef
#undef #undef
You can then set the elements as usual, e.g.,
m[1,2] = KeyVal(1,2)
If it makes sense for your type to implement a zero method, you can define
Base.zero(::Type{KeyVal}) = KeyVal(0,0)
and zeros will work correctly
julia> zeros(KeyVal, (2,2))
2×2 Array{KeyVal,2}:
KeyVal(0,0.0) KeyVal(0,0.0)
KeyVal(0,0.0) KeyVal(0,0.0)
Warning:
zeros uses fill! which fills the array with copies of the same instance. Instead, use the comprehension below or an immutable type that doesn't contain references (isbits is true). The latter approach is likely faster as well because the array can be stored as one continuous block of memory. In that case, you can also use fill(KeyVal(0,0), dims..) directly instead of defining zero. On 0.6, the new struct keyword creates an immutable type by default.
Edit:
Another possibility is to use a comprehension
julia> m = [KeyVal() for i=1:3, j=1:2]
3×2 Array{KeyVal,2}:
KeyVal(0,0.0) KeyVal(0,0.0)
KeyVal(0,0.0) KeyVal(0,0.0)
KeyVal(0,0.0) KeyVal(0,0.0)
For convenience, I previously defined the outer constructor
KeyVal() = KeyVal(0,0)

Julia: append to an empty vector

I would like to create an empty vector and append to it an array in Julia. How do I do that?
x = Vector{Float64}
append!(x, rand(10))
results in
`append!` has no method matching append!(::Type{Array{Float64,1}}, ::Array{Float64,1})
Thanks.
Your variable x does not contain an array but a type.
x = Vector{Float64}
typeof(x) # DataType
You can create an array as Array(Float64, n)
(but beware, it is uninitialized: it contains arbitrary values) or zeros(Float64, n),
where n is the desired size.
Since Float64 is the default, we can leave it out.
Your example becomes:
x = zeros(0)
append!( x, rand(10) )
I am somewhat new to Julia and came across this question after getting a similar error. To answer the original question for Julia version 1.2.0, all that is missing are ():
x = Vector{Float64}()
append!(x, rand(10))
This solution (unlike x=zeros(0)) works for other data types, too. For example, to create an empty vector to store dictionaries use:
d = Vector{Dict}()
push!(d, Dict("a"=>1, "b"=>2))
A note regarding use of push! and append!:
According to the Julia help, push! is used to add individual items to a collection, while append! adds an collection of items to a collection. So, the following pieces of code create the same array:
Push individual items:
a = Vector{Float64}()
push!(a, 1.0)
push!(a, 2.0)
Append items contained in an array:
a = Vector{Float64}()
append!(a, [1.0, 2.0])
You can initialize an empty Vector of any type by typing the type in front of []. Like:
Float64[] # Returns what you want
Array{Float64, 2}[] # Vector of Array{Float64,2}
Any[] # Can contain anything
New answer, for Julia 1. append! is deprecated, you now need to use push!(array, element) to add elements to an array
my_stuff = zeros()
push!(my_stuff, "new element")

Using more than one, "for x in y" in the same row. Syntax issue

What I want to do is simple:
collection = {'a':[], 'b':[], 'c':[]}
values = [1,2,3]
I want to make a function that produces the following: (append the values into the list element of the dictionary, the dic and the list are the same length)
{'a':[1], 'b':[2], 'c':[3]}
This is simple enough and I can do it using, a couple of for x in. But I want to do this in one line. (using two loops in the same line) and I can not get the syntax to work.
I have tried some things similar to this, but they all result in syntax error:
collection[c].append(value), for c in d.iteritems(), for value in values
You can't do what you want to do on one line. You can create a new dictionary on one line though:
{k: collection[k] + [v] for k, v in zip(collection.keys(), values)}
Result:
>>> collection = {'a':[], 'b':[], 'c':[]}
>>> values = [1,2,3]
>>> {k: collection[k] + [v] for k, v in zip(collection.keys(), values)}
{'a': [1], 'c': [2], 'b': [3]}
This is called a dict comprehension. Like a list comprehension and a generator expression, you can use multiple loops in one of those, but you don't need one here. zip() will pair up the keys from collection with the integers from values.
To modify a dict in-place, you'll have to use 2 lines at least:
for k, v in zip(collection.keys(), values):
collection[k].append(v)
Python does accept that on one line, but that goes against just about every styleguide I can look up for you:
for k, v in zip(collection.keys(), values): collection[k].append(v)
Python throws a syntax error because it interprets your line as a tuple of expressions (the commas make it a tuple), and two of your expressions are for statements, which cannot be used in an expression.

Resources