From ecd12e45112ca54b6ce19ffec737e489b89ac81d Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Thu, 23 Jan 2014 21:38:16 -0800 Subject: [PATCH 1/3] Added get! for Dicts * Refactored setindex! into ht_keyindex2, for use by get! and setindex! --- base/dict.jl | 113 +++++++++++++++++++++++++++----------------- test/collections.jl | 10 ++++ 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 154726b013f7c..33e78f4639666 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -419,13 +419,34 @@ function empty!{K,V}(h::Dict{K,V}) return h end -function setindex!{K,V}(h::Dict{K,V}, v0, key0) - key = convert(K,key0) - if !isequal(key,key0) - error(key0, " is not a valid key for type ", K) +# get the index where a key is stored, or -1 if not present +function ht_keyindex{K,V}(h::Dict{K,V}, key) + sz = length(h.keys) + iter = 0 + maxprobe = max(16, sz>>6) + index = hashindex(key, sz) + keys = h.keys + + while true + if isslotempty(h,index) + break + end + if !isslotmissing(h,index) && isequal(key,keys[index]) + return index + end + + index = (index & (sz-1)) + 1 + iter+=1 + iter > maxprobe && break end - v = convert(V, v0) + return -1 +end + +# get the index where a key is stored, or -pos if not present +# and the key would be inserted at pos +# This version is for use by setindex! and get! +function ht_keyindex2{K,V}(h::Dict{K,V}, key) sz = length(h.keys) if h.ndel >= ((3*sz)>>2) || h.count*3 > sz*2 @@ -437,28 +458,23 @@ function setindex!{K,V}(h::Dict{K,V}, v0, key0) iter = 0 maxprobe = max(16, sz>>6) index = hashindex(key, sz) - avail = -1 # an available slot - keys = h.keys; vals = h.vals + avail = 0 + keys = h.keys while true if isslotempty(h,index) - if avail > 0; index = avail; end - h.slots[index] = 0x1 - h.keys[index] = key - h.vals[index] = v - h.count += 1 - return h + avail < 0 && return avail + return -index end if isslotmissing(h,index) - if avail<0 + if avail == 0 # found an available slot, but need to keep scanning # in case "key" already exists in a later collided slot. - avail = index + avail = -index end elseif isequal(key, keys[index]) - vals[index] = v - return h + return index end index = (index & (sz-1)) + 1 @@ -466,42 +482,51 @@ function setindex!{K,V}(h::Dict{K,V}, v0, key0) iter > maxprobe && break end - if avail>0 - index = avail - h.slots[index] = 0x1 - h.keys[index] = key - h.vals[index] = v - h.count += 1 - return h - end + avail < 0 && return avail rehash(h, h.count > 64000 ? sz*2 : sz*4) - setindex!(h, v, key) + return ht_keyindex2(h, key) end -# get the index where a key is stored, or -1 if not present -function ht_keyindex{K,V}(h::Dict{K,V}, key) - sz = length(h.keys) - iter = 0 - maxprobe = max(16, sz>>6) - index = hashindex(key, sz) - keys = h.keys +function _setindex!(h::Dict, v, key, index) + h.slots[index] = 0x1 + h.keys[index] = key + h.vals[index] = v + h.count += 1 +end - while true - if isslotempty(h,index) - break - end - if !isslotmissing(h,index) && isequal(key,keys[index]) - return index - end +function setindex!{K,V}(h::Dict{K,V}, v0, key0) + key = convert(K,key0) + if !isequal(key,key0) + error(key0, " is not a valid key for type ", K) + end + v = convert(V, v0) - index = (index & (sz-1)) + 1 - iter+=1 - iter > maxprobe && break + index = ht_keyindex2(h, key) + + if index > 0 + h.vals[index] = v + else + _setindex!(h, v, key, -index) end - return -1 + return h +end + +function get!{K,V}(h::Dict{K,V}, key0, default) + key = convert(K,key0) + if !isequal(key,key0) + error(key0, " is not a valid key for type ", K) + end + + index = ht_keyindex2(h, key) + + index > 0 && return h.vals[index] + + v = convert(V, default) + _setindex!(h, v, key, -index) + return v end function getindex{K,V}(h::Dict{K,V}, key) diff --git a/test/collections.jl b/test/collections.jl index 0b0d2879bceff..30393bd4be25b 100644 --- a/test/collections.jl +++ b/test/collections.jl @@ -147,6 +147,16 @@ d4[1001] = randstring(3) @test !isequal({1 => 2}, {"dog" => "bone"}) @test isequal(Dict{Int, Int}(), Dict{String, String}()) +# get! (get with default values assigned to the given location) + +let + d = {8=>19} + def = {} + @test get!(d, 8, 5) == 19 + @test get!(d, 19, 2) == 2 + @test d == {8=>19, 19=>2} +end + # issue #2540 d = {x => 1 for x in ['a', 'b', 'c']} From 1c9ca01bb173dda24674649bb45c815dfb55f1e5 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Thu, 30 Jan 2014 19:45:51 -0800 Subject: [PATCH 2/3] Add docs, do-block function for get! --- base/dict.jl | 15 +++++++++++++++ doc/stdlib/base.rst | 15 +++++++++++++++ test/collections.jl | 5 ++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/base/dict.jl b/base/dict.jl index 33e78f4639666..045fb8b23d0e8 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -529,6 +529,21 @@ function get!{K,V}(h::Dict{K,V}, key0, default) return v end +function get!{K,V}(default::Function, h::Dict{K,V}, key0) + key = convert(K,key0) + if !isequal(key,key0) + error(key0, " is not a valid key for type ", K) + end + + index = ht_keyindex2(h, key) + + index > 0 && return h.vals[index] + + v = convert(V, default()) + _setindex!(h, v, key, -index) + return v +end + function getindex{K,V}(h::Dict{K,V}, key) index = ht_keyindex(h, key) return (index<0) ? throw(KeyError(key)) : h.vals[index]::V diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index a4a360dc354fb..3ed6541a49754 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -727,6 +727,21 @@ Given a dictionary ``D``, the syntax ``D[x]`` returns the value of key ``x`` (if Return the value stored for the given key, or the given default value if no mapping for the key is present. +.. function:: get!(collection, key, default) + + Return the value stored for the given key, or if no mapping for the key is present, store ``key => default``, and return ``default``. + +.. function:: get!(f::Function, collection, key) + + Return the value stored for the given key, or if no mapping for the key is present, store ``key => f()``, and return ``f()``. + + This is intended to be called using ``do`` block syntax:: + + get!(dict, key) do + # default value calculated here + time() + end + .. function:: getkey(collection, key, default) Return the key matching argument ``key`` if one exists in ``collection``, otherwise return ``default``. diff --git a/test/collections.jl b/test/collections.jl index 30393bd4be25b..f7f1872d5f4bc 100644 --- a/test/collections.jl +++ b/test/collections.jl @@ -154,7 +154,10 @@ let def = {} @test get!(d, 8, 5) == 19 @test get!(d, 19, 2) == 2 - @test d == {8=>19, 19=>2} + @test get!(d, 42) do + int(e^2) + end == 7 + @test d == {8=>19, 19=>2, 42=>7} end # issue #2540 From 72a65be4ef9fe58adc13a91e0241a4e2f22ab9f7 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Sat, 8 Feb 2014 15:19:53 -0800 Subject: [PATCH 3/3] Add docs, do-block function for get Also added get, get! for WeakKeyDicts --- base/dict.jl | 8 ++++++++ doc/stdlib/base.rst | 11 +++++++++++ test/collections.jl | 19 +++++++++++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 045fb8b23d0e8..3f0f5eab507b7 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -554,6 +554,11 @@ function get{K,V}(h::Dict{K,V}, key, deflt) return (index<0) ? deflt : h.vals[index]::V end +function get{K,V}(deflt::Function, h::Dict{K,V}, key) + index = ht_keyindex(h, key) + return (index<0) ? deflt() : h.vals[index]::V +end + haskey(h::Dict, key) = (ht_keyindex(h, key) >= 0) in{T<:Dict}(key, v::KeyIterator{T}) = (ht_keyindex(v.dict, key) >= 0) @@ -665,6 +670,9 @@ function getkey{K}(wkh::WeakKeyDict{K}, kk, deflt) end get{K}(wkh::WeakKeyDict{K}, key, def) = get(wkh.ht, key, def) +get{K}(def::Function, wkh::WeakKeyDict{K}, key) = get(def, wkh.ht, key) +get!{K}(wkh::WeakKeyDict{K}, key, def) = get!(wkh.ht, key, def) +get!{K}(def::Function, wkh::WeakKeyDict{K}, key) = get!(def, wkh.ht, key) pop!{K}(wkh::WeakKeyDict{K}, key) = pop!(wkh.ht, key) pop!{K}(wkh::WeakKeyDict{K}, key, def) = pop!(wkh.ht, key, def) delete!{K}(wkh::WeakKeyDict{K}, key) = delete!(wkh.ht, key) diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 3ed6541a49754..02af58c01f1c2 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -727,6 +727,17 @@ Given a dictionary ``D``, the syntax ``D[x]`` returns the value of key ``x`` (if Return the value stored for the given key, or the given default value if no mapping for the key is present. +.. function:: get(f::Function, collection, key) + + Return the value stored for the given key, or if no mapping for the key is present, return ``f()``. Use ``get!`` to also store the default value in the dictionary. + + This is intended to be called using ``do`` block syntax:: + + get(dict, key) do + # default value calculated here + time() + end + .. function:: get!(collection, key, default) Return the value stored for the given key, or if no mapping for the key is present, store ``key => default``, and return ``default``. diff --git a/test/collections.jl b/test/collections.jl index f7f1872d5f4bc..e60d759ab6243 100644 --- a/test/collections.jl +++ b/test/collections.jl @@ -150,14 +150,25 @@ d4[1001] = randstring(3) # get! (get with default values assigned to the given location) let + f(x) = x^2 d = {8=>19} def = {} @test get!(d, 8, 5) == 19 @test get!(d, 19, 2) == 2 - @test get!(d, 42) do - int(e^2) - end == 7 - @test d == {8=>19, 19=>2, 42=>7} + + @test get!(d, 42) do # d is updated with f(2) + f(2) + end == 4 + + @test get!(d, 42) do # d is not updated + f(200) + end == 4 + + @test get(d, 13) do # d is not updated + f(4) + end == 16 + + @test d == {8=>19, 19=>2, 42=>4} end # issue #2540