Golang 切片 使用函数式 实现 filter map find reduce 等方法

13 min read

在 Golang 中,切片是一种非常常用的数据类型,它提供了方便的 API 以操作数据。但是,如果想要使用函数式编程的风格来操作切片,需要自己实现 filter、map、find、reduce 等方法。下面是使用函数式实现这些方法的示例:

定义一个接口类型,用于实现泛型:

type predicate func(interface{}) bool
type mapper func(interface{}) interface{}
type reducer func(interface{}, interface{}) interface{}

这里定义了三种函数类型:predicate、mapper 和 reducer,它们分别代表了过滤器函数、映射函数和归约函数。接下来分别实现对应的 filter、map 和 reduce 方法:

func Filter(s interface{}, p predicate) interface{} {
    v := reflect.ValueOf(s)
    if v.Kind() != reflect.Slice {
        panic("argument is not a slice")
    }

    result := reflect.MakeSlice(v.Type(), 0, v.Len())
    for i := 0; i < v.Len(); i++ {
        item := v.Index(i).Interface()
        if p(item) {
            result = reflect.Append(result, v.Index(i))
        }
    }

    return result.Interface()
}

func Map(s interface{}, m mapper) interface{} {
    v := reflect.ValueOf(s)
    if v.Kind() != reflect.Slice {
        panic("argument is not a slice")
    }

    result := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(m(nil))), 0, v.Len())
    for i := 0; i < v.Len(); i++ {
        item := v.Index(i).Interface()
        mapped := m(item)
        result = reflect.Append(result, reflect.ValueOf(mapped))
    }

    return result.Interface()
}

func Reduce(s interface{}, r reducer, acc interface{}) interface{} {
    v := reflect.ValueOf(s)
    if v.Kind() != reflect.Slice {
        panic("argument is not a slice")
    }

    if acc == nil {
        acc = reflect.Zero(reflect.TypeOf(v.Index(0).Interface())).Interface()
    }

    for i := 0; i < v.Len(); i++ {
        item := v.Index(i).Interface()
        acc = r(acc, item)
    }

    return acc
}

在 filter 和 map 方法中,我们使用了反射来获取切片的类型和值,并遍历每个元素执行相应的函数逻辑,最后返回新的切片。在 reduce 方法中,我们首先判断是否传入初始值,如果没有则取切片中第一个元素的值作为初始值。然后遍历每个元素执行归约函数逻辑,最后返回归约的结果。

下面是一个示例使用 filter、map 和 reduce 方法的示例:

func main() {
    s := []int{1, 2, 3, 4, 5}

    filtered := Filter(s, func(v interface{}) bool {
        return v.(int) % 2 == 0
    }).([]int)
    fmt.Println(filtered) // [2 4]

    mapped := Map(s, func(v interface{}) interface{} {
        return v.(int) * 2
    }).([]int)
    fmt.Println(mapped) // [2 4 6 8 10]

    reduced := Reduce(s, func(acc interface{}, v interface{}) interface{} {
        return acc.(int) + v.(int)
    }, nil).(int)
    fmt.Println(reduced) // 15
}

可以看到,我们成功使用函数式编程的风格来操作切片,相比于传统的 for 循环写法,更加简洁和易读。