dourunlao1642
dourunlao1642
2017-06-02 08:59

如何在Julia中将husky()和in()函数与复杂的字典键一起使用?

haskey() and in() functions are very useful to test the content of dictionaries in Julia :

julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5)
Dict{String,Int64} with 5 entries:
  "c" => 3
  "e" => 5
  "b" => 2
  "a" => 1
  "d" => 4

julia> haskey(dict, "a")
true

julia> in(("a" => 1), dict)
true

but I was surprised by their behavior with complex keys :

julia> immutable MyT
           A::String
           B::Int64
       end

julia> a = Dict(MyT("Tom",191)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1)
Dict{MyT,Int64} with 4 entries:
  MyT("Tom",191) => 1
  MyT("Jo",315)  => 1
  MyT("Bob",20)  => 1
  MyT("Luc",493) => 1

julia> keys(a)
Base.KeyIterator for a Dict{MyT,Int64} with 4 entries. Keys:
  MyT("Tom",191)
  MyT("Jo",315)
  MyT("Bob",20)
  MyT("Luc",493)

julia> haskey(a, MyT("Tom",191))
false

julia> in((MyT("Tom",191) => 1), a)
false

What I did wrong ? Thank you very much for your comments !

Thanks to @Michael K. Borregaard, I can propose this solution :

a = Dict{MyT, Int64}()

keyArray = Array{MyT,1}()
keyArray = [MyT("Tom",191),MyT("Bob",20),MyT("Jo",315),MyT("Luc",493)]

for i in keyArray
    a[i] = 1
end

println(a)
# Dict(MyT("Tom",191)=>1,MyT("Tom",191)=>1,MyT("Luc",493)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Bob",20)=>1)

keyArray[1]            # MyT("Tom",191)
haskey(a, keyArray[1]) # true

But I have to store keys in a separate array. This means that can't warranty the unicity of the keys which is the strength of the dictionaries and why I choose to use it :(

So I have to use another step :

unique(keyArray)

Another better solution :

function CompareKeys(k1::MyT, k2::MyT)
    if k1.A == k2.A &&  k1.B == k2.B
        return true
    else 
        return false
    end
end

function ExistKey(k::MyT, d::Dict{MyT, Int64})
    for i in keys(d)
        if CompareKeys(k, i)
            return true
        end
    end
    return false
end

a = Dict(MyT("Tom",191)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1)

ExistKey(MyT("Tom",192),a) # false

ExistKey(MyT("Tom",191),a) # true

Compared to Julia, Go is more straightforward for this problem :

package main

import (
    "fmt"
)

type MyT struct {
    A string
    B int
}

func main() {

    dic := map[MyT]int{MyT{"Bob", 10}: 1, MyT{"Jo", 21}: 1}

    if _, ok := dic[MyT{"Bob", 10}]; ok {
        fmt.Println("key exists")
    }
}
// answer is "key exists"
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

4条回答

  • doujing1156 doujing1156 4年前

    You just need to teach your MyT type that you want it to consider equality in terms of its composite fields:

    julia> immutable MyT
               A::String
               B::Int64
           end
           import Base: ==, hash
           ==(x::MyT, y::MyT) = x.A == y.A && x.B == y.B
           hash(x::MyT, h::UInt) = hash(x.A, hash(x.B, hash(0x7d6979235cb005d0, h)))
    
    julia> a = Dict(MyT("Tom",191)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1)
    Dict{MyT,Int64} with 4 entries:
      MyT("Jo", 315)  => 1
      MyT("Luc", 493) => 1
      MyT("Tom", 191) => 1
      MyT("Bob", 20)  => 1
    
    julia> haskey(a, MyT("Tom",191))
    true
    
    julia> in((MyT("Tom",191) => 1), a)
    true
    
    点赞 评论 复制链接分享
  • dssnh86244 dssnh86244 4年前

    My approach would be similar to Matt's, but a bit simpler(?). Tuples are perfectly valid dictionary keys, so I would simply overload the relevant functions to convert your type back and forth to a tuple:

    julia> immutable M; A::String; B::Int64; end
    julia> import Base: =>, haskey, in
    julia> =>(a::M, b) = (a.A, a.B)=>b
    julia> haskey(a::Dict, b::M) = haskey(a, (b.A, b.B))
    julia> in(a::Pair{M, Int64}, b::Int64) = in((a.first.A,a.first.B)=>a.second,b)
    
    julia> a = Dict(M("Dick", 10)=>1, M("Harry", 20)=>2)
    Dict{Tuple{String,Int64},Int64} with 2 entries:
      ("Dick", 10)  => 1
      ("Harry", 20) => 2
    
    julia> haskey(a, M("Dick", 10))
    true
    
    julia> in(M("Dick", 10)=>1, a)
    true
    

    "Compared to Julia, Go is more straightforward for this problem"

    True. It also happens to be more error-prone (depending on your perspective). If you wanted to differentiate between two objects (used as keys) that do not correspond to the same object in memory, then Go's approach of simply testing 'value equality' would have landed you in trouble here (though one could argue 'value equality' generally makes more sense when comparing 'keys').

    点赞 评论 复制链接分享
  • dongshi2836 dongshi2836 4年前

    You're creating a new object in the haskey call. But two objects created by MyT("Tom", 191) are just two different MyT objects with the same field values.

    Instead, do

    key1 = MyT("Tom", 191)
    a = Dict(key1 => 1)
    haskey(a, key1)
    

    see also

    key2 = MyT("Tom", 191)
    key1 == key2 # false
    

    A julia-ideomatic way to deal with this would be to define an == method for MyT objects, so two objects are equal if they have the same field values. That would allow you to use them like you do.

    It depends whether you need the type to be complex. Another easy and performant way to do what you want is to use a Tuple as the key:

    a = Dict(("Tom", 191) => 1)
    haskey(a, ("Tom", 191)) # true
    a[("Tom", 191)] # 1
    
    点赞 评论 复制链接分享
  • dqj96395 dqj96395 4年前

    There are lots of good answers here, I'd just like to add a subtlety: this is partly because == calls === rather than recursively calling == when checking for structural equality, and partly because equal (==) strings are not generally identical (===) currently. Specifically, the fact that MyT("foo", 1) != MyT("foo", 1) is because "foo" !== "foo".

    Strings are only "immutable by convention" – they are technically mutable, but Julia doesn't expose APIs for mutating them and encourages you not to mutate them. You can, however, access their underlying bytes and mutate that, which allows you to write a program that distinguishes two strings by getting by mutating one and not the other. That means that they cannot be === in the sense of Henry Baker's "EGAL" predicate (also here). If you have an immutable type with only "primitive" type fields, then this does not happen:

    julia> immutable MyT2 # `struct MyT2` in 0.6
               A::Float64
               B::Int
           end
    
    julia> x = MyT2(1, 1)
    MyT2(1.0, 1)
    
    julia> y = MyT2(1, 1)
    MyT2(1.0, 1)
    
    julia> x == y
    true
    
    julia> x === y
    true
    

    I have already proposed that we change this and have == recursively call ==. This should be fixed, someone just needs to do the work. Moreover, in Julia 1.0 we could make Strings truly immutable rather than merely immutable by convention, and therefore have "foo" === "foo" be true. I've created an issue to discuss and track this change.

    点赞 评论 复制链接分享