Home > Blockchain >  How do I do this recursion in golang
How do I do this recursion in golang

Time:11-02

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
}()

CodePudding user response:

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.

CodePudding user response:

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{} {...}
}
  • Related