funcEmployeeCountIf(list []Employee, fn func(e *Employee)bool) int { count := 0 for i, _ := range list { if fn(&list[i]) { count += 1 } } return count }
funcEmployeeFilterIn(list []Employee, fn func(e *Employee)bool) []Employee { var newList []Employee for i, _ := range list { if fn(&list[i]) { newList = append(newList, list[i]) } } return newList }
funcEmployeeSumIf(list []Employee, fn func(e *Employee)int) int { var sum = 0 for i, _ := range list { sum += fn(&list[i]) } return sum }
funcTransform(slice, function interface{})interface{} { return transform(slice, function, false) }
funcTransformInPlace(slice, function interface{})interface{} { return transform(slice, function, true) }
functransform(slice, function interface{}, inPlace bool)interface{} { //check the `slice` type is Slice sliceInType := reflect.ValueOf(slice) if sliceInType.Kind() != reflect.Slice { panic("transform: not slice") }
//check the function signature fn := reflect.ValueOf(function) elemType := sliceInType.Type().Elem() if !verifyFuncSignature(fn, elemType, nil) { panic("trasform: function must be of type func(" + sliceInType.Type().Elem().String() + ") outputElemType") }
sliceOutType := sliceInType if !inPlace { sliceOutType = reflect.MakeSlice(reflect.SliceOf(fn.Type().Out(0)), sliceInType.Len(), sliceInType.Len()) } for i := 0; i < sliceInType.Len(); i++ { sliceOutType.Index(i).Set(fn.Call([]reflect.Value{sliceInType.Index(i)})[0]) } return sliceOutType.Interface()
//Check it is a funciton if fn.Kind() != reflect.Func { returnfalse } // NumIn() - returns a function type's input parameter count. // NumOut() - returns a function type's output parameter count. if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) { returnfalse } // In() - returns the type of a function type's i'th input parameter. for i := 0; i < len(types)-1; i++ { if fn.Type().In(i) != types[i] { returnfalse } } // Out() - returns the type of a function type's i'th output parameter. outType := types[len(types)-1] if outType != nil && fn.Type().Out(0) != outType { returnfalse } returntrue }
代码一下子就复杂起来了,可见,复杂的代码都是在处理异常的地方。
我来列一下代码中的几个要点。代码中没有使用 Map 函数,因为和数据结构有含义冲突的问题,所以使用Transform,这个来源于 C++ STL 库中的命名。有两个版本的函数,一个是返回一个全新的数组 Transform(),一个是“就地完成” TransformInPlace()。
funcReduce(slice, pairFunc, zero interface{})interface{} { sliceInType := reflect.ValueOf(slice) if sliceInType.Kind() != reflect.Slice { panic("reduce: wrong type, not slice") }
len := sliceInType.Len() iflen == 0 { return zero } elseiflen == 1 { return sliceInType.Index(0) }
elemType := sliceInType.Type().Elem() fn := reflect.ValueOf(pairFunc) if !verifyFuncSignature(fn, elemType, elemType, elemType) { t := elemType.String() panic("reduce: function must be of type func(" + t + ", " + t + ") " + t) }
var ins [2]reflect.Value ins[0] = sliceInType.Index(0) ins[1] = sliceInType.Index(1) out := fn.Call(ins[:])[0]
for i := 2; i < len; i++ { ins[0] = out ins[1] = sliceInType.Index(i) out = fn.Call(ins[:])[0] } return out.Interface() }
funcFilter(slice, function interface{})interface{} { result, _ := filter(slice, function, false) return result }
funcFilterInPlace(slicePtr, function interface{}) { in := reflect.ValueOf(slicePtr) if in.Kind() != reflect.Ptr { panic("FilterInPlace: wrong type, " + "not a pointer to slice") } _, n := filter(in.Elem().Interface(), function, true) in.Elem().SetLen(n) }
var boolType = reflect.ValueOf(true).Type()
funcfilter(slice, function interface{}, inPlace bool)(interface{}, int) {
sliceInType := reflect.ValueOf(slice) if sliceInType.Kind() != reflect.Slice { panic("filter: wrong type, not a slice") }
fn := reflect.ValueOf(function) elemType := sliceInType.Type().Elem() if !verifyFuncSignature(fn, elemType, boolType) { panic("filter: function must be of type func(" + elemType.String() + ") bool") }
var which []int for i := 0; i < sliceInType.Len(); i++ { if fn.Call([]reflect.Value{sliceInType.Index(i)})[0].Bool() { which = append(which, i) } }
out := sliceInType
if !inPlace { out = reflect.MakeSlice(sliceInType.Type(), len(which), len(which)) } for i := range which { out.Index(i).Set(sliceInType.Index(which[i])) }