changeset 749:688767a6f3eb

Merge feature/static_dict
author Vidar Stiernström <vidar.stiernstrom@it.uu.se>
date Fri, 19 Mar 2021 16:40:30 +0100
parents 6dd9f97fc2be (current diff) 9807799592d4 (diff)
children f88b2117dc69 0159a91fc55a
files
diffstat 3 files changed, 151 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/Sbplib.jl	Wed Mar 17 22:21:01 2021 +0100
+++ b/src/Sbplib.jl	Fri Mar 19 16:40:30 2021 +0100
@@ -1,5 +1,6 @@
 module Sbplib
 
+include("StaticDicts/StaticDicts.jl")
 include("RegionIndices/RegionIndices.jl")
 include("LazyTensors/LazyTensors.jl")
 include("Grids/Grids.jl")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/StaticDicts/StaticDicts.jl	Fri Mar 19 16:40:30 2021 +0100
@@ -0,0 +1,82 @@
+module StaticDicts
+
+export StaticDict
+
+"""
+    StaticDict{K,V,N} <: AbstractDict{K,V}
+
+A static dictionary implementing the interface for an `AbstractDict`. A
+`StaticDict` is fully immutable and after creation no changes can be made.
+
+The immutable nature means that `StaticDict` can be compared with `===`, in
+constrast to regular `Dict` or `ImmutableDict` which can not. (See
+https://github.com/JuliaLang/julia/issues/4648 for details) One important
+aspect of this is that `StaticDict` can be used in a struct while still
+allowing the struct to be comared using the default implementation of `==` for
+structs.
+
+Lookups are done by linear search.
+
+Duplicate keys are not allowed and an error will be thrown if they are passed
+to the constructor.
+"""
+struct StaticDict{K,V,N} <: AbstractDict{K,V}
+    pairs::NTuple{N,Pair{K,V}}
+
+    function StaticDict{K,V}(pairs::Vararg{Pair,N}) where {K,V,N}
+        if !allunique(first.(pairs))
+            throw(DomainError(pairs, "keys must be unique"))
+        end
+        return new{K,V,N}(pairs)
+    end
+end
+
+function StaticDict(pairs::Vararg{Pair})
+    K = typejoin(firsttype.(pairs)...)
+    V = typejoin(secondtype.(pairs)...)
+    return StaticDict{K,V}(pairs...)
+end
+
+StaticDict(pairs::NTuple{N,Pair} where N) = StaticDict(pairs...)
+
+function Base.get(d::StaticDict, key, default)
+    for p ∈ d.pairs
+        if key == p.first
+            return p.second
+        end
+    end
+
+    return default
+end
+
+Base.iterate(d::StaticDict) = iterate(d.pairs)
+Base.iterate(d::StaticDict, state) = iterate(d.pairs,state)
+Base.length(d::StaticDict) = length(d.pairs)
+
+
+"""
+    merge(d1::StaticDict, d2::StaticDict)
+
+Merge two `StaticDict`. Repeating keys is considered and error. This may
+change in a future version.
+"""
+function Base.merge(d1::StaticDict, d2::StaticDict)
+    return StaticDict(d1.pairs..., d2.pairs...)
+end
+
+
+"""
+    firsttype(::Pair{T1,T2})
+
+The type of the first element in the pair.
+"""
+firsttype(::Pair{T1,T2}) where {T1,T2} = T1
+
+"""
+    secondtype(::Pair{T1,T2})
+
+The type of the secondtype element in the pair.
+"""
+secondtype(::Pair{T1,T2}) where {T1,T2}  = T2
+
+end # module
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/StaticDicts/StaticDicts_test.jl	Fri Mar 19 16:40:30 2021 +0100
@@ -0,0 +1,68 @@
+using Test
+using Sbplib.StaticDicts
+
+@testset "StaticDicts" begin
+
+@testset "StaticDict" begin
+    @testset "constructor" begin
+        @test (StaticDict{Int,Int,N} where N) <: AbstractDict
+
+        d = StaticDict(1=>2, 3=>4)
+        @test d isa StaticDict{Int,Int}
+        @test d[1] == 2
+        @test d[3] == 4
+
+        @test StaticDict((1=>2, 3=>4)) == d
+
+        @test StaticDict() isa StaticDict
+        @test StaticDict{Int,String}() isa StaticDict{Int,String,0}
+
+        @test StaticDict(1=>3, 2=>4.) isa StaticDict{Int,Real}
+        @test StaticDict(1. =>3, 2=>4) isa StaticDict{Real,Int}
+        @test StaticDict(1. =>3, 2=>4.) isa StaticDict{Real,Real}
+
+        @test_throws DomainError StaticDict(1=>3, 1=>3)
+    end
+
+    @testset "length" begin
+        @test length(StaticDict()) == 0
+        @test length(StaticDict(1=>1)) == 1
+        @test length(StaticDict(1=>1, 2=>2)) == 2
+    end
+
+    @testset "equality" begin
+        @test StaticDict(1=>1) == StaticDict(1=>1)
+        @test StaticDict(2=>1) != StaticDict(1=>1)
+        @test StaticDict(1=>2) != StaticDict(1=>1)
+
+        @test StaticDict(1=>1) === StaticDict(1=>1) #not true for a regular Dict
+        @test StaticDict(2=>1) !== StaticDict(1=>1)
+        @test StaticDict(1=>2) !== StaticDict(1=>1)
+    end
+
+    @testset "get" begin
+        d = StaticDict(1=>2, 3=>4)
+
+        @test get(d,1,6) == 2
+        @test get(d,3,6) == 4
+        @test get(d,5,6) == 6
+    end
+
+    @testset "iterate" begin
+        pairs = [1=>2, 3=>4, 5=>6]
+
+        d = StaticDict(pairs...)
+        @test collect(d) == pairs
+    end
+
+    @testset "merge" begin
+        @test merge(
+            StaticDict(1=>3, 2=> 4),
+            StaticDict(3=>5,4=>6)) == StaticDict(
+                1=>3, 2=>4, 3=>5, 4=>6
+            )
+        @test_throws DomainError merge(StaticDict(1=>3),StaticDict(1=>3))
+    end
+end
+
+end