英文:
How do I do this recursion in golang
问题
我有以下的Go代码,我试图进行递归操作。然而,Go编译器报错说有循环引用。当然,我有循环引用;这是递归。
我尝试将递归移到主函数内和外,但问题依然存在。我该如何实现这个递归?错误出现在这一行:return lookup(deref.Interface()) // recursion error 'cyclic definition'
christianb@christianb-mac terraform-provider% go build
# github.com/terraform-provider/pkg/foo/repositories.go:773:5: initialization loop:
/Users/christianb/dev/terraform-provider/pkg/foo/repositories.go:773:5: lookup refers to
/Users/christianb/dev/terraform-provider/pkg/foo/repositories.go:774:18: glob..func6.1 refers to
/Users/christianb/dev/terraform-provider/pkg/foo/artifactory/repositories.go:773:5: lookup
type AutoMapper func(field reflect.StructField, thing reflect.Value) map[string]interface{}
var lookup = func() func(payload interface{}) map[string]interface{} {
var handlePtr = func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
deref := reflect.Indirect(thing)
if deref.CanAddr() {
if deref.Kind() == reflect.Struct {
return lookup(deref.Interface()) // recursion error 'cyclic definition'
}
return map[string]interface{}{
field.Tag.Get("hcl"): deref.Interface(),
}
}
return map[string]interface{}{}
}
var checkForHcl = func(mapper AutoMapper) AutoMapper {
return func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
if field.Tag.Get("hcl") != "" {
return mapper(field, thing)
}
return map[string]interface{}{}
}
}
lk := map[reflect.Kind]AutoMapper{}
find := func(payload interface{}) map[string]interface{} {
values := map[string]interface{}{}
var t = reflect.TypeOf(payload)
var v = reflect.ValueOf(payload)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
thing := v.Field(i)
for key, value := range lk[thing.Kind()](field, thing) {
values[key] = value
}
}
return values
}
lk[reflect.Struct] = checkForHcl(func(f reflect.StructField, t reflect.Value) map[string]interface{} {
return find(t.Interface())
})
lk[reflect.Slice] = checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): thing.Interface().([]string),
}
})
lk[reflect.Ptr] = checkForHcl(handlePtr)
return find
}()
英文:
I have the below go code and I am trying to do a recursion. However, the go compiler is complaining of a cyclic reference. Of course I have a cyclic reference; it's a recursion.
I have tried moving the recursion into the main function and out. Same issue. How can I achieve this recursion? The error line is: return lookup(deref.Interface()) // recursion error 'cyclic definition'
christianb@christianb-mac terraform-provider% go build
# github.com/terraform-provider/pkg/foo/repositories.go:773:5: initialization loop:
/Users/christianb/dev/terraform-provider/pkg/foo/repositories.go:773:5: lookup refers to
/Users/christianb/dev/terraform-provider/pkg/foo/repositories.go:774:18: glob..func6.1 refers to
/Users/christianb/dev/terraform-provider/pkg/foo/artifactory/repositories.go:773:5: lookup
type AutoMapper func(field reflect.StructField, thing reflect.Value) map[string]interface{}
var lookup = func() func(payload interface{}) map[string]interface{} {
var handlePtr = func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
deref := reflect.Indirect(thing)
if deref.CanAddr() {
if deref.Kind() == reflect.Struct {
return lookup(deref.Interface()) // recursion error 'cyclic definition'
}
return map[string]interface{}{
field.Tag.Get("hcl"): deref.Interface(),
}
}
return map[string]interface{}{}
}
var checkForHcl = func(mapper AutoMapper) AutoMapper {
return func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
if field.Tag.Get("hcl") != "" {
return mapper(field, thing)
}
return map[string]interface{}{}
}
}
lk := map[reflect.Kind]AutoMapper{}
find := func(payload interface{}) map[string]interface{} {
values := map[string]interface{}{}
var t = reflect.TypeOf(payload)
var v = reflect.ValueOf(payload)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
thing := v.Field(i)
for key, value := range lk[thing.Kind()](field, thing) {
values[key] = value
}
}
return values
}
lk[reflect.Struct] = checkForHcl(func(f reflect.StructField, t reflect.Value) map[string]interface{} {
return find(t.Interface())
})
lk[reflect.Slice] = checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): thing.Interface().([]string),
}
})
lk[reflect.Ptr] = checkForHcl(handlePtr)
return find
}()
答案1
得分: 3
你的函数不能通过名称引用自身,因为它是一个匿名函数。只有当它被赋值给变量lookup
后,它才会变成有名字的函数。在词法上,这只会在被赋值的值完全解析之后发生。这与普通的func
声明不同,普通的声明中函数名立即可用(这使得递归更加清晰):
func myFunc(arg) result {
// ... 做一些事情 ...
// 现在我们可以递归调用函数
return myFunc(arg)
}
在你的情况下,普通的func
声明不起作用,所以你需要一种"前向声明"来使名称可用,这需要一小部分重复代码:
// 前向声明函数
var myFunc func(arg) result
myFunc = func(arg) result {
// ... 做一些事情 ...
// 现在我们可以递归调用函数
return myFunc(arg)
}
要在全局上下文中做同样的事情,参见Burak的答案。
英文:
Your function cannot reference itself by name, because it is an anonymous function. It only becomes named once it gets assigned to the variable lookup
. Lexically, this only happens once the value being assigned is fully parsed. This is different from a normal func
declaration, where the name is available immediately (which makes recursion rather cleaner):
func myFunc(arg) result {
// ... do something ...
// now we can call the function recursively
return myFunc(arg)
}
In your case, a regular func
declaration won't work, so you need some kind of "forward declaration" to make the name available, which requires a small amount of duplication:
// forward declare the function
var myFunc func(arg) result
myFunc = func(arg) result {
// ... do something ...
// now we can call the function recursively
return myFunc(arg)
}
To do the same thing in a global context, see Burak's answer.
答案2
得分: 1
问题是一个初始化循环,而不是递归。你正在引用变量的定义中的变量。你可以这样做:
var lookup func() func(payload interface{}) map[string]interface{}
func init() {
lookup=func() func(payload interface{}) map[string]interface{} {...}
}
英文:
The problem is an initialization loop, not recursion. You are referring to a variable from that variable's definition. You can do:
var lookup func() func(payload interface{}) map[string]interface{}
func init() {
lookup=func() func(payload interface{}) map[string]interface{} {...}
}
答案3
得分: 0
我按照其他人的建议以各种排列方式尝试了一下,但问题依然存在。最终,我找到了解决方法:
type AutoMapper func(field reflect.StructField, thing reflect.Value) map[string]interface{}
func checkForHcl(mapper AutoMapper) AutoMapper {
return func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
if field.Tag.Get("hcl") != "" {
return mapper(field, thing)
}
return map[string]interface{}{}
}
}
func findInspector(kind reflect.Kind) AutoMapper {
switch kind {
case reflect.Struct:
return checkForHcl(func(f reflect.StructField, t reflect.Value) map[string]interface{} {
return lookup(t.Interface())
})
case reflect.Ptr:
return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
deref := reflect.Indirect(thing)
if deref.CanAddr() {
if deref.Kind() == reflect.Struct {
return lookup(deref.Interface())
}
return map[string]interface{}{
field.Tag.Get("hcl"): deref.Interface(),
}
}
return map[string]interface{}{}
})
case reflect.Slice:
return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): castToInterfaceArr(thing.Interface().([]string)),
}
})
}
return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): thing.Interface(),
}
})
}
func lookup(payload interface{}) map[string]interface{} {
values := map[string]interface{}{}
var t = reflect.TypeOf(payload)
var v = reflect.ValueOf(payload)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
thing := v.Field(i)
typeInspector := findInspector(thing.Kind())
for key, value := range typeInspector(field, thing) {
values[key] = value
}
}
return values
}
英文:
I did as other suggested in various permutations and got the same issue. Ultimately, this is what I got to work
type AutoMapper func(field reflect.StructField, thing reflect.Value) map[string]interface{}
func checkForHcl(mapper AutoMapper) AutoMapper {
return func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
if field.Tag.Get("hcl") != "" {
return mapper(field, thing)
}
return map[string]interface{}{}
}
}
func findInspector(kind reflect.Kind) AutoMapper {
switch kind {
case reflect.Struct:
return checkForHcl(func(f reflect.StructField, t reflect.Value) map[string]interface{} {
return lookup(t.Interface())
})
case reflect.Ptr:
return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
deref := reflect.Indirect(thing)
if deref.CanAddr() {
if deref.Kind() == reflect.Struct {
return lookup(deref.Interface())
}
return map[string]interface{}{
field.Tag.Get("hcl"): deref.Interface(),
}
}
return map[string]interface{}{}
})
case reflect.Slice:
return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): castToInterfaceArr(thing.Interface().([]string)),
}
})
}
return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): thing.Interface(),
}
})
}
func lookup(payload interface{}) map[string]interface{} {
values := map[string]interface{}{}
var t = reflect.TypeOf(payload)
var v = reflect.ValueOf(payload)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
thing := v.Field(i)
typeInspector := findInspector(thing.Kind())
for key, value := range typeInspector(field, thing) {
values[key] = value
}
}
return values
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论