如何在golang中实现递归?

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

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 &#39;cyclic definition&#39;

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 &#39;cyclic definition&#39;
			}
			return map[string]interface{}{
				field.Tag.Get(&quot;hcl&quot;): 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(&quot;hcl&quot;) != &quot;&quot; {
				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 &lt; 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(&quot;hcl&quot;): 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(&quot;hcl&quot;) != &quot;&quot; {
			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(&quot;hcl&quot;): 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(&quot;hcl&quot;): castToInterfaceArr(thing.Interface().([]string)),
			}
		})
	}
	return checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
		return map[string]interface{}{
			field.Tag.Get(&quot;hcl&quot;): 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 &lt; 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
}

huangapple
  • 本文由 发表于 2021年11月2日 10:35:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/69804785.html
匿名

发表评论

匿名网友

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

确定