我试图实现一个通用搜索算法,该算法将同时遍历任意树状数据结构(为树的每个级别启动一个go例程)。
我遇到的问题是搜索失败时(它完成整个树的行走而发现没有匹配)我最终陷入僵局,我不明白为什么。
搜索成功时,代码正常工作。
我为你的上下文添加了辅助函数,但是我的问题在于我使用的是例程,它们都在concurrentSearch()函数中。
如果您发现代码杂乱,我会提前告知。我一整天都来回尝试不同的想法。
感谢您的反馈!
这是我的代码:
package search
import "reflect"
// Search an arbitrary tree / map like data structure.
func concurrentSearch(output chan interface{}, data interface{}, key string) {
// search() is your search algorithm.
result := search(data, key)
if result != nil {
// Found something, so put it on the chan.
// This is the point of the function.
output <- result
return // Success.
}
// Since we didn't find a match we will get a slice of the next level of values.
iterableType := listValues(data)
if len(iterableType) == 0 {
return
}
var wg sync.WaitGroup
for _, value := range iterableType {
wg.Add(1)
go func(next interface{}) {
defer wg.Done()
concurrentSearch(output, next, key)
}(value)
}
wg.Wait()
// Wait never finishes, so the function never returns
}
// Returns a slice of values based on Type.
func listValues(data interface{}) []interface{} {
value := reflect.ValueOf(data)
values := []interface{}{}
switch value.Kind() {
case reflect.Map:
for _, key := range value.MapKeys() {
values = append(values, value.MapIndex(key).Interface())
}
case reflect.Slice:
for ii := 0; ii < value.Len(); ii++ {
values = append(values, value.Index(ii).Interface())
}
case reflect.Struct:
for ii := 0; ii < value.NumField(); ii++ {
if value.Field(ii).CanSet() {
values = append(values, value.Field(ii).Interface())
}
}
case reflect.Ptr:
rawValue := value.Elem()
if !rawValue.IsValid() {
return nil
}
values = listValues(rawValue)
case reflect.Interface:
rawValue := value.Elem()
values = listValues(rawValue)
}
return values
}
// search just checks the value for an index of key, otherwise returns nil.
func search(data interface{}, key string) interface{} {
value := reflect.ValueOf(data)
switch value.Kind() {
// If the type is indexable, check the index.
// Default to returning nil
case reflect.Struct:
for ii := 0; ii < value.NumField(); ii++ {
field := value.Field(ii)
// Without CanSet() reflect panics about Unexported fields.
if field.CanSet() {
if field.Type().Name() == key {
return field.Elem().Interface()
}
}
}
return nil
case reflect.Map:
for _, mapkey := range value.MapKeys() {
if key == mapkey.String() {
return value.MapIndex(mapkey).Elem().Interface()
}
}
return nil
case reflect.Slice:
return nil
// For pointer types we just unwrap and call again.
case reflect.Ptr:
rawValue := value.Elem()
if !rawValue.IsValid() {
return nil
}
return search(rawValue, key)
case reflect.Interface:
rawValue := value.Elem()
return search(rawValue, key)
case reflect.String:
return nil
default:
return nil
}
}
这是我的测试代码,遗憾的是我无法共享测试数据。它只是一堆嵌套的json对象。
package state
import (
"encoding/json"
"io/ioutil"
"reflect"
"testing"
)
const (
dataPath = "path/to/your/data.json"
)
var tests = []struct {
Input string
Result interface{}
}{
// your test fixtures here for table driven tests.
}
type mydata struct {
One map[string]interface{} `json:"1"`
Two map[string]interface{} `json:"2"`
Three map[string]interface{} `json:"3"`
}
func TestConcurrentSearch(t *testing.T) {
data := &mydata{
map[string]interface{}{},
map[string]interface{}{},
map[string]interface{}{},
}
bytes, err := ioutil.ReadFile(dataPath)
if err != nil {
t.Errorf("ioutil: problem loading test data, %v", err)
}
jerr := json.Unmarshal(bytes, data)
if jerr != nil {
t.Errorf("json: problem unmarshalling test data, %s", jerr)
}
// Setup finished
output := make(chan interface{})
for _, fixture := range tests {
go concurrentSearch(output, data, fixture.Input)
select {
case result := <-output:
if !reflect.DeepEqual(fixture.Result, result) {
t.Errorf("Expected: %v, Actual %v", fixture.Result, result)
}
}
}
}