本文介绍了Go而不是Julia复杂的关键词典的唯一性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在GO中,当我将结构用作地图的键时,键是唯一的.

In GO when I use a struct as a key for a map, there is an unicity of the keys.

例如,以下代码生成仅具有一个键的地图:map [{x 1}:1]

For example, the following code produce a map with only one key : map[{x 1}:1]

package main

import (
    "fmt"
)

type MyT struct {
    A string
    B int
}

func main() {

    dic := make(map[MyT]int)

    for i := 1; i <= 10; i++ {
        dic[MyT{"x", 1}] = 1
    }

    fmt.Println(dic)
}

// result : map[{x 1}:1]

我试图在朱莉娅(Julia)中做同样的事情,但我感到很奇怪:

I Tried to do the same in Julia and I had a strange surprise :

此Julia代码类似于GO代码,可生成具有10个键的字典!

This Julia code, similar to the GO one, produces a dictionary whith 10 keys !

    type MyT
        A::String
        B::Int64
    end

    dic = Dict{MyT, Int64}()

    for i in 1:10
        dic[MyT("x", 1)] = 1
    end

    println(dic)
    # Dict(MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1,MyT("x",1)=>1)

    println(keys(dic))
    # MyT[MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1),MyT("x",1)]

那我做错了什么?

感谢@DanGetz提供解决方案! :

Thank you @DanGetz for the solution ! :

immutable MyT     # or struct MyT with julia > 0.6
    A::String
    B::Int64
end

dic = Dict{MyT, Int64}()

for i in 1:10
    dic[MyT("x", 1)] = 1
end

println(dic)         # Dict(MyT("x", 1)=>1)
println(keys(dic))   # MyT[MyT("x", 1)]

推荐答案

可变值在Julia中通过身份哈希 ,因为如果没有其他关于类型表示的知识,就无法知道两个值是否与相同的结构意味着相同或不同.如果在将值用作字典键之后对值进行突变,则按值对可变对象进行哈希处理可能会特别成问题.按身份哈希时,这并不是问题,因为可变对象的身份即使修改后也保持不变.另一方面,按值对不可变对象进行散列是绝对安全的-因为它们不能被突变,因此,这是不可变类型的默认行为.在给定的示例中,如果将MyT设为不可变,则将自动获得您期望的行为:

Mutable values hash by identity in Julia, since without additional knowledge about what a type represents, one cannot know if two values with the same structure mean the same thing or not. Hashing mutable objects by value can be especially problematic if you mutate a value after using it as a dictionary key – this is not a problem when hashing by identity since the identity of a mutable object remains the same even when it is modified. On the other hand, it's perfectly safe to hash immutable objects by value – since they cannot be mutated, and accordingly that is the default behavior for immutable types. In the given example, if you make MyT immutable you will automatically get the behavior you're expecting:

immutable MyT # `struct MyT` in 0.6
    A::String
    B::Int64
end

dic = Dict{MyT, Int64}()

for i in 1:10
    dic[MyT("x", 1)] = 1
end
julia> dic
Dict{MyT,Int64} with 1 entry:
  MyT("x", 1) => 1

julia> keys(dic)
Base.KeyIterator for a Dict{MyT,Int64} with 1 entry. Keys:
  MyT("x", 1)

对于要用作哈希键的包含StringInt值的类型,不变性可能是正确的选择.实际上,不变性通常是正确的选择,这就是为什么引入结构类型的关键字在不可变结构中更改为0.6到struct,在可变结构中更改为mutable struct的原因-人们会追求首先使用更短,更简单的名称,因此应该是更好的默认选择-即不变性.

For a type holding a String and an Int value that you want to use as a hash key, immutability is probably the right choice. In fact, immutability is the right choice more often than not, which is why the keywords introducing structural types has been change in 0.6 to struct for immutable structures and mutable struct for mutable structures – on the principle that people will reach for the shorter, simpler name first, so that should be the better default choice – i.e. immutability.

正如@ntdef所写,您可以通过重载Base.hash函数来更改您的类型的哈希行为.但是,他的定义在某些方面是不正确的(这可能是我们的错误,因为未能更清晰,更彻底地记录下来).

As @ntdef has written, you can change the hashing behavior of your type by overloading the Base.hash function. However, his definition is incorrect in a few respects (which is probably our fault for failing to document this more prominently and thoroughly):

  1. 要重载的Base.hash的方法签名是Base.hash(::T, ::UInt).
  2. Base.hash(::T, ::UInt)方法必须返回UInt值.
  3. 如果要重载Base.hash,还应该重载Base.==才能匹配.
  1. The method signature of Base.hash that you want to overload is Base.hash(::T, ::UInt).
  2. The Base.hash(::T, ::UInt) method must return a UInt value.
  3. If you are overloading Base.hash, you should also overload Base.== to match.

因此,这将是按值使可变类型散列的正确方法(重新定义MyT需要新的Julia会话):

So this would be a correct way to make your mutable type hash by value (new Julia session required to redefine MyT):

type MyT # `mutable struct MyT` in 0.6
    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((MyT, x.A, x.B), h)

dic = Dict{MyT, Int64}()

for i in 1:10
    dic[MyT("x", 1)] = 1
end
julia> dic
Dict{MyT,Int64} with 1 entry:
  MyT("x", 1) => 1

julia> keys(dic)
Base.KeyIterator for a Dict{MyT,Int64} with 1 entry. Keys:
  MyT("x", 1)

这很烦人,但是 AutoHashEquals 软件包可以自动完成此操作,枯燥乏味.您需要做的就是在type定义前面加上@auto_hash_equals宏:

This is kind of annoying to do manually, but the AutoHashEquals package automates this, taking the tedium out of it. All you need to do is prefix the type definition with the @auto_hash_equals macro:

using AutoHashEquals

@auto_hash_equals type MyT # `@auto_hash_equals mutable struct MyT` in 0.6
    A::String
    B::Int64
end

底线:

  • 如果您的类型应具有基于值的相等性和散列,请认真考虑使其变为不可变的.

  • If you have a type that should have value-based equality and hashing, seriously consider making it immutable.

如果您的类型确实必须是可变的,那么请认真考虑将其用作哈希键是否是个好主意.

If your type really has to be mutable, then think hard about whether it's a good idea to use as a hash key.

如果您确实需要使用可变类型作为具有基于值的相等性和哈希语义的哈希键,请使用AutoHashEquals包.

If you really need to use a mutable type as a hash key with value-based equality and hashing semantics, use the AutoHashEquals package.

这篇关于Go而不是Julia复杂的关键词典的唯一性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 21:30