Golang:如何创建未知(动态)的 Map 长度

huangapple go评论66阅读模式
英文:

Golang: How to create unknown (dynamic) Map length

问题

我可以通过以下方式创建一个“静态”地图:

type m map[int]map[int]map[int]bool

但是,“键”的长度将是动态的:

m[1][2][3][4][2][0] = true

或者

m[1][2][3][4] = true

我如何在Go中创建这个地图?或者是否存在其他方法?

补充说明:层次结构是重要的

提前感谢!

英文:

I can create a "static" map via

type m map[int]map[int]map[int]bool

but the length of "keys" will be dynamic:

 |---unknown len--|
m[1][2][3][4][2][0] = true

or

|---unk len--|
m[1][2][3][4] = true

How I can create this map in Go? Or any way exists?

Added: Hierarchical is IMPORTANT

Thanks in advance!

答案1

得分: 3

map类型

map是一组无序的元素,其中每个元素都有一个类型,称为元素类型,由另一个类型的一组唯一键索引,称为键类型。

map类型必须具有特定的值类型和特定的键类型。你想要的不符合这个要求:你想要一个值有时是另一个map(相同类型的),有时是一个bool的map。

你有以下选择:

1. 使用包装值类型

这里的想法是不仅仅使用一个简单的(bool)值类型,而是使用一个同时包含两个潜在值的包装类型:一个map和一个简单值(bool):

type Value struct {
    Children MapType
    V        bool
}

type MapType map[int]*Value

var m MapType

这基本上就是用户user3591723建议的,所以我不会详细解释了。

2. 使用树

这是#1的一个变体,但这样我们可以清楚地表达它是一棵树。

实现你的层次结构的最干净的方法是使用一棵树,其中一个节点可以是这样的:

type KeyType int
type ValueType string

type Node struct {
    Children map[KeyType]*Node
    Value    ValueType
}

这样做的好处是你可以选择值类型(在你的情况下是bool,但你可以将其更改为任何类型 - 我在这里使用了string进行演示)。

为了方便地构建/管理你的树,我们可以为我们的Node类型添加一些方法:

func (n *Node) Add(key KeyType, v ValueType) {
    if n.Children == nil {
        n.Children = map[KeyType]*Node{}
    }
    n.Children[key] = &Node{Value: v}
}

func (n *Node) Get(keys ...KeyType) *Node {
    for _, key := range keys {
        n = n.Children[key]
    }
    return n
}

func (n *Node) Set(v ValueType, keys ...KeyType) {
    n = n.Get(keys...)
    n.Value = v
}

使用它:1. 构建一棵树,2. 查询一些值,3. 更改一个值:

root := &Node{Value: "root"}
root.Add(0, "first")
root.Get(0).Add(9, "second")
root.Get(0, 9).Add(3, "third")
root.Get(0).Add(4, "fourth")

fmt.Println(root)
fmt.Println(root.Get(0, 9, 3))
fmt.Println(root.Get(0, 4))

root.Set("fourthMod", 0, 4)
fmt.Println(root.Get(0, 4))

输出(在Go Playground上尝试):

&{map[0:0x104382f0] root}
&{map[] third}
&{map[] fourth}
&{map[] fourthMod}

3. 使用递归类型定义

令人惊讶的是,在Go中可以定义一个具有无限或动态“深度”的map类型,使用递归定义:

type X map[int]X

它的含义就是:它是一个具有int键和与map本身相同类型的值的map

这个递归类型的一个很大的缺点是它不能在值类型中存储任何“有用”的数据。它只能存储“事实”,即值是否存在,这与bool类型的信息相同(bool类型:truefalse),在某些情况下可能足够,但大多数情况下不够。

让我们看一个构建“树”的例子:

var x X
x = map[int]X{}
x[0] = map[int]X{}
x[0][9] = map[int]X{}
x[0][9][3] = map[int]X{}
x[0][4] = map[int]X{}
fmt.Println(x)

输出:

map[0:map[9:map[3:map[]] 4:map[]]]

如果我们想要测试是否存在基于一系列键的“值”,我们有两个选择:要么使用特殊的v, ok := m[i]索引(报告指定键的值是否存在),要么测试值是否不为nil,例如m[i] != nil

让我们看一些测试上述构建的map的示例:

var ok bool
_, ok = x[0][9][3]
fmt.Println("x[0][9][3] exists:", ok, "; alternative way:", x[0][9][3] != nil)
_, ok = x[0][9][4]
fmt.Println("x[0][9][4] exists:", ok, "; alternative way:", x[0][9][4] != nil)
_, ok = x[0][4]
fmt.Println("x[0][4] exists:", ok, "; alternative way:", x[0][4] != nil)
_, ok = x[0][4][9][9][9]
fmt.Println("x[0][4][9][9][9] exists:", ok, "; alternative way:", x[0][4][9][9][9] != nil)

输出:

x[0][9][3] exists: true ; alternative way: true
x[0][9][4] exists: false ; alternative way: false
x[0][4] exists: true ; alternative way: true
x[0][4][9][9][9] exists: false ; alternative way: false

Go Playground上尝试这些。

**注意:**即使x[0][4]是最后一个“叶子”,进一步索引如x[0][4][9][9][9]也不会引发恐慌,因为可以对nil map进行索引,并产生值类型的零值(如果值类型是map类型,则为nil)。

英文:

The map type:

> A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type.

A map type must have a specific value type and a specific key type. What you want does not qualify for this: you want a map where the value is sometimes another map (of the same type), and sometimes it's a bool.

Your options:

1. With a wrapper value type

The idea here is to not use just a simple (bool) value type, but a wrapper which holds both of your potential values: both a map and the simple value (bool):

type Value struct {
	Children MapType
	V        bool
}

type MapType map[int]*Value

var m MapType

This is basically what user3591723 suggested, so I won't detail it further.

2. With a tree

This is a variant of #1, but this way we clearly communicate it's a tree.

The cleanest way to implement your hierarchical structure would be to use a tree, where a node could look like this:

type KeyType int
type ValueType string

type Node struct {
	Children map[KeyType]*Node
	Value    ValueType
}

This has the advantage that you may choose the value type (which is bool in your case, but you can change it to whatever type - I used string for presentation).

For easily build / manage your tree, we can add some methods to our Node type:

func (n *Node) Add(key KeyType, v ValueType) {
	if n.Children == nil {
		n.Children = map[KeyType]*Node{}
	}
	n.Children[key] = &Node{Value: v}
}

func (n *Node) Get(keys ...KeyType) *Node {
	for _, key := range keys {
		n = n.Children[key]
	}
	return n
}

func (n *Node) Set(v ValueType, keys ...KeyType) {
	n = n.Get(keys...)
	n.Value = v
}

And using it: 1. build a tree, 2. query some values, 3. change a value:

root := &Node{Value: "root"}
root.Add(0, "first")
root.Get(0).Add(9, "second")
root.Get(0, 9).Add(3, "third")
root.Get(0).Add(4, "fourth")

fmt.Println(root)
fmt.Println(root.Get(0, 9, 3))
fmt.Println(root.Get(0, 4))

root.Set("fourthMod", 0, 4)
fmt.Println(root.Get(0, 4))

Output (try it on the Go Playground):

&{map[0:0x104382f0] root}
&{map[] third}
&{map[] fourth}
&{map[] fourthMod}

3. With a recursive type definition

It may be surprising but it is possible to define a map type in Go which has unlimited or dynamic "depth", using a recursive definition:

type X map[int]X

It is what it says: it's a map with int keys, and values of the same type as the map itself.

The big downside of this recursive type is that it can't store any "useful" data in the value type. It can only store the "fact" whether a value is present which is identical to a bool-like information (bool type: true or false), which may be enough in rare cases, but not in most.

Let's see an example building a "tree":

var x X
x = map[int]X{}
x[0] = map[int]X{}
x[0][9] = map[int]X{}
x[0][9][3] = map[int]X{}
x[0][4] = map[int]X{}
fmt.Println(x)

Output:

map[0:map[9:map[3:map[]] 4:map[]]]

If we want to test if there is a "value" based on a series of keys, we have 2 options: either use the special v, ok := m[i] indexing (which reports if a value for the specified key exists), or test if the value is not nil, e.g. m[i] != nil.

Let's see some examples testing the above built map:

var ok bool
_, ok = x[0][9][3]
fmt.Println("x[0][9][3] exists:", ok, "; alternative way:", x[0][9][3] != nil)
_, ok = x[0][9][4]
fmt.Println("x[0][9][4] exists:", ok, "; alternative way:", x[0][9][4] != nil)
_, ok = x[0][4]
fmt.Println("x[0][4] exists:", ok, "; alternative way:", x[0][4] != nil)
_, ok = x[0][4][9][9][9]
fmt.Println("x[0][4][9][9][9] exists:", ok, "; alternative way:", x[0][4][9][9][9] != nil)

Output:

x[0][9][3] exists: true ; alternative way: true
x[0][9][4] exists: false ; alternative way: false
x[0][4] exists: true ; alternative way: true
x[0][4][9][9][9] exists: false ; alternative way: false

Try these on the Go Playground.

Note: Even though x[0][4] is the last "leaf", indexing further like x[0][4][9][9][9] will not cause a panic as a nil map can be indexed and yields the zero value of the value type (which is nil in case the value type is a map type).

答案2

得分: 1

好的,以下是翻译好的内容:

好的,我玩得很开心,稍微改进了一下。这是比之前更好的实现方式:

type mymap map[int]*myentry

type myentry struct {
    m mymap
    b bool
}

func (mm mymap) get(idx ...int) *myentry {
    if len(idx) == 0 {
        return nil
    }
    entry, ok := mm[idx[0]]
    if !ok {
        return nil
    } else if len(idx) == 1 {
        return entry
    }
    for i := 1; i < len(idx); i++ {
        if entry == nil || entry.m == nil {
            return nil
        }
        entry = entry.m[idx[i]]
    }
    return entry
}

func (mm mymap) setbool(v bool, idx ...int) {
    if len(idx) == 0 {
        return
    }
    if mm[idx[0]] == nil {
        mm[idx[0]] = &myentry{m: make(mymap), b: false}
    } else if mm[idx[0]].m == nil {
        mm[idx[0]].m = make(mymap)
    }
    if len(idx) == 1 {
        mm[idx[0]].b = v
        return
    }
    entry := mm[idx[0]]
    for i := 1; i < len(idx); i++ {
        if entry.m == nil {
            entry.m = make(mymap)
            entry.m[idx[i]] = &myentry{m: make(mymap), b: false}
        } else if entry.m[idx[i]] == nil {
            entry.m[idx[i]] = &myentry{m: make(mymap), b: false}
        }
        entry = entry.m[idx[i]]
    }
    entry.b = v
}

func (m mymap) getbool(idx ...int) bool {
    if val := m.get(idx...); val != nil {
        return val.b
    }
    return false
}

func (m mymap) getmap(idx ...int) mymap {
    if val := m.get(idx...); val != nil {
        return val.m
    }
    return nil
}

Playground链接

这样的代码应该能帮助你入门。

英文:

Ok I had some fun playing with this a bit. Here is a much better implementation than what I did before:

type mymap map[int]*myentry
type myentry struct {
m mymap
b bool
}
func (mm mymap) get(idx ...int) *myentry {
if len(idx) == 0 {
return nil
}
entry, ok := mm[idx[0]]
if !ok {
return nil
} else if len(idx) == 1 {
return entry
}
for i := 1; i &lt; len(idx); i++ {
if entry == nil || entry.m == nil {
return nil
}
entry = entry.m[idx[i]]
}
return entry
}
func (mm mymap) setbool(v bool, idx ...int) {
if len(idx) == 0 {
return
}
if mm[idx[0]] == nil {
mm[idx[0]] = &amp;myentry{m: make(mymap), b: false}
} else if mm[idx[0]].m == nil {
mm[idx[0]].m = make(mymap)
}
if len(idx) == 1 {
mm[idx[0]].b = v
return
}
entry := mm[idx[0]]
for i := 1; i &lt; len(idx); i++ {
if entry.m == nil {
entry.m = make(mymap)
entry.m[idx[i]] = &amp;myentry{m: make(mymap), b: false}
} else if entry.m[idx[i]] == nil {
entry.m[idx[i]] = &amp;myentry{m: make(mymap), b: false}
}
entry = entry.m[idx[i]]
}
entry.b = v
}
func (m mymap) getbool(idx ...int) bool {
if val := m.get(idx...); val != nil {
return val.b
}
return false
}
func (m mymap) getmap(idx ...int) mymap {
if val := m.get(idx...); val != nil {
return val.m
}
return nil
}

Playground link

Something like that ought to get you started

答案3

得分: 0

如果您不需要层次结构的映射结构,只想使用具有可变长度的键,一种方法是简单地将字符串作为键和一个单一的映射使用。

m := make(map[string]bool)
k := fmt.Sprintf("%v_%v_%v", 1, 2, 3)

m[k] = true

fmt.Println(m[k])

如果您有任何其他问题,请随时提问。

英文:

If you don't need the hierarchical map structure and just want to use keys with variable length one approach could be to simply use strings as keys and one single map.

m := make(map[string]bool)
k := fmt.Sprintf(&quot;%v_%v_%v&quot;, 1, 2, 3)
m[k] = true
fmt.Println(m[k])

答案4

得分: 0

你无法这样做,因为这种类型在Go的类型系统中无法表示。

你将不得不重新设计。

例如,使用一个名为arbitrarilyKeyedMap的类型,并添加一个名为lookup(vals ...int) bool的方法。可能你还需要添加设置和删除的方法。

英文:

You cannot do this as this sort of type is not representable in Go's type system.

You will have to redesign.

E.g. a type arbitrarilyKeyedMapwith a method lookup(vals ...int) bool.
Probably you'll need methods for setting and deletion too.

huangapple
  • 本文由 发表于 2016年1月13日 15:52:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/34761129.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定