golang 函数式小工具

简介

项目地址
代码比较简单。主要就是用反射实现了一下函数式。
因为go没有泛型的原因,只能返回interface{}。这对代码提示不太友好
所以几乎每个函数都有一个2版本。2取自TO就是用一个变量去装结果。
这样就可以有类型信息了。

主要功能

  • Fmap 对一个列表进行映射
  • Fold 对一个列表应用一个累加器
  • Filter 根据函数,对一个列表进行过滤
  • Flat 如果是一个嵌套列表,对他展开
  • ZipWith 通过函数合并两个列表
  • Unzip 分裂列表为两个
  • Pipe 取前面参数作为第一个函数参数,结果和剩下参数作为第二个函数参数

具体用法看测试代码

未来版本

未来可能会重构一下。循环用并发执行。

代码

/*
简易函数式编程库
Simple functional programming library
*/
package fp

import (
	"fmt"
	"reflect"
)

/*
将arr中每个元素包装为reflect.Value数组以便于反射调用。
*/
/*
Wrap each element in the ARR as reflect Value array for reflection calls.
*/
func prepare(arr []interface{
    
    }, num int) [][]reflect.Value {
    
    
	ps := make([][]reflect.Value, len(arr))
	for i := range arr {
    
    
		a, t := toArr(arr[i]), make([]reflect.Value, num)
		for j := range a {
    
    
			t[j] = reflect.ValueOf(a[j])
		}
		ps[i] = t
	}
	return ps
}

/*
fx 过滤函数, arr待处理列表。
用fx过滤arr中元素。
fx返回为真时保留,假时剔除。
*/
/*
FX filter function, arr pending list.
Filter elements in arr with FX.
If FX returns true, it will be retained and if false, it will be rejected.
*/
func Filter(fx interface{
    
    }, arr interface{
    
    }) []interface{
    
    } {
    
    
	f := reflect.ValueOf(fx)
	aa := toArr(arr)
	ps := prepare(aa, f.Type().NumIn())
	res := make([]interface{
    
    }, 0, len(ps)/2+1)
	for i := range aa {
    
    
		if f.Call(ps[i])[0].Interface().(bool) {
    
    
			res = append(res, aa[i])
		}
	}
	return res
}

/*
把 a 转换为数组。
若a不是数组,则用一个长度为1的数组包装。
*/ 
/*
Convert a to an array.
If a is not an array, wrap it with an array of length 1.
*/
func toArr(a interface{
    
    }) (res []interface{
    
    }) {
    
    
	v := reflect.ValueOf(a)
	switch v.Kind() {
    
    
	case reflect.Array, reflect.Slice:
		res = make([]interface{
    
    }, v.Len())
		for i := range res {
    
    
			res[i] = v.Index(i).Interface()
		}
	default:
		res = []interface{
    
    }{
    
    a}
	}
	return
}

/*
fx 过滤函数, arr待处理列表,resPtr 用于存储结果的地址,返回值为结果长度。
与Filter函数作用一样,只不过可以通过resPtr存储结果,并指定类型。
需要注意的是resPtr如果是切片或数组类型需要预留好足够空间。
*/
/*
FX filter function, arr pending list, resptr address used to store results,
The return value is the result length.
The function is the same as the filter function, except that the result can be stored through resptr and the type can be specified.
It should be noted that if resptr is a slice or array type, enough space should be reserved.
*/
func Filter2(fx, arr, resPtr interface{
    
    }) int {
    
    
	t := Filter(fx, arr)
	fill(resPtr, t...)
	return len(t)
}

/*
展开arr中各个元素
*/
/*
Expand the elements in the arr
*/
func Flat(arr interface{
    
    }) []interface{
    
    } {
    
    
	a := toArr(arr)
	res := make([]interface{
    
    }, 0, len(a))
	for i := range a {
    
    
		res = append(res, toArr(a[i])...)
	}
	return res
}

/*
展开arr中各个元素,并把结果存到resPtr。返回值为结果长度
*/
/*
Expand the elements in arr and save the results to resptr. The return value is the result length
*/
func Flat2(arr interface{
    
    }, resPtr interface{
    
    }) int {
    
    
	t := Flat(arr)
	fill(resPtr, t...)
	return len(t)
}

/*
对arr中每个元素应用一次fx
*/
/*
Apply FX once to each element in the arr
*/
func Fmap(fx interface{
    
    }, arr interface{
    
    }) []interface{
    
    } {
    
    
	f := reflect.ValueOf(fx)
	j := f.Type().NumOut()
	ps := prepare(toArr(arr), f.Type().NumIn())
	res := make([]interface{
    
    }, len(ps))
	if j == 1 {
    
    
		for i := range res {
    
    
			res[i] = f.Call(ps[i])[0].Interface()
		}
	} else {
    
    
		for i := range res {
    
    
			outs := f.Call(ps[i])
			t := make([]interface{
    
    }, j)
			for x := range t {
    
    
				t[x] = outs[x].Interface()
			}
			res[i] = t
		}
	}
	return res
}

/*
通过fx函数合并arr1和arr2
*/
/*
Merge Arr1 and arr2 through FX function
*/
func ZipWith(fx, arr1, arr2 interface{
    
    }) []interface{
    
    } {
    
    
	a1, a2 := toArr(arr1), toArr(arr2)
	n := len(a1)
	if n > len(a2) {
    
    
		n = len(a2)
	}
	res := make([]interface{
    
    }, n)
	f := reflect.ValueOf(fx)
	for i := range res {
    
    
		res[i] = f.Call([]reflect.Value{
    
    reflect.ValueOf(a1[i]), reflect.ValueOf(a2[i])})[0].Interface()
	}
	return res
}

/*
通过fx函数合并arr1和arr2,并把结果存到resPtr。返回值为结果长度
*/
/*
Merge Arr1 and arr2 through the FX function and save the results in resptr. The return value is the result length.
*/
func ZipWith2(fx, arr1, arr2, resPtr interface{
    
    }) int {
    
    
	t := ZipWith(fx, arr1, arr2)
	fill(resPtr, t...)
	return len(t)
}

/*
对arr中的元素两两应用fx函数。
若arr长度为0返回nil,长度为1返回原数组。
*/
/*
Apply the FX function to every two elements in the arr.
If the ARR length is 0, nil is returned, and if the ARR length is 1, the original array is returned.
*/
func Fold(fx, arr interface{
    
    }) interface{
    
    } {
    
    
	a := toArr(arr)
	n := len(a)
	if n == 0 {
    
    
		return nil
	}
	f, res := reflect.ValueOf(fx), a[0]
	for i := 1; i < n; i++ {
    
    
		res = f.Call([]reflect.Value{
    
    reflect.ValueOf(res), reflect.ValueOf(a[i])})[0].Interface()
	}
	return res
}

/*
对arr中的元素两两应用fx函数,结果存入resPtr。
若arr长度为0返回nil,长度为1返回原数组。
*/
/*
Apply the FX function to two elements in arr, and store the result in resptr.
If the ARR length is 0, nil is returned, and if the ARR length is 1, the original array is returned.
*/
func Fold2(fx, arr, resPtr interface{
    
    }) {
    
    
	t := Fold(fx, arr)
	fill(resPtr, t)
}

/*
把arr内容填充到ptr指向的对象。
*/
/*
Fill the ARR content into the object pointed to by PTR.
*/
func fill(ptr interface{
    
    }, arr ...interface{
    
    }) {
    
    
	v := reflect.ValueOf(ptr).Elem()
	switch v.Kind() {
    
    
	case reflect.Array, reflect.Slice:
		for i := range arr {
    
    
			v.Index(i).Set(reflect.ValueOf(arr[i]))
		}
	default:
		v.Set(reflect.ValueOf(arr[0]))
	}
}

/*
对arr中每个元素应用一次fx,并把结果存到resPtr。返回值为结果长度。
*/
/*
Apply FX to each element in arr once and save the result in resptr. The return value is the result length.
*/
func Fmap2(fx, arr, resPtr interface{
    
    }) int {
    
    
	t := Fmap(fx, arr)
	fill(resPtr, t...)
	return len(t)
}

/*
半函数。
*/
/*
Function with partial parameters fixed.
*/
type HalfFunc func(...interface{
    
    }) interface{
    
    }

/*
p 为参数列表,fx 为函数。
返回一个HalfFunc。
Currying可以固定一个函数的前几个参数。
*/
/*
P is the parameter list and FX is the function. Returns a HalfFunc.
Currying can fix the first few parameters of a function.
*/
func Currying(p, fx interface{
    
    }) HalfFunc {
    
    
	f := reflect.ValueOf(fx)
	return func(arr ...interface{
    
    }) interface{
    
    } {
    
    
		pp := toArr(p)
		n, pl := len(arr)+len(pp), len(pp)
		if n != f.Type().NumIn() {
    
    
			msg := fmt.Sprintf("Takes %d positional argument but %d were given\n", f.Type().NumIn(), n)
			panic("Wrong number of parameters\n" + msg)
		}
		ps := make([]reflect.Value, n)
		for i := range pp {
    
    
			ps[i] = reflect.ValueOf(pp[i])
		}
		for i := pl; i < n; i++ {
    
    
			ps[i] = reflect.ValueOf(arr[i-pl])
		}
		t := Fmap(reflect.Value.Interface, f.Call(ps))
		if len(t) == 1 {
    
    
			return t[0]
		}
		return t
	}
}

/*
fx函数应接收两个参数,一个为resPtr,另一个为arr中的元素。
该函数遍历arr,并调用fx函数。
*/
/*
The FX function should take two arguments, one resptr and the other an element in arr.
This function traverses the ARR and calls the FX function.
*/
func Reduce(resPtr, fx, arr interface{
    
    }) {
    
    
	a, f := toArr(arr), reflect.ValueOf(fx)
	for i := range a {
    
    
		f.Call([]reflect.Value{
    
    reflect.ValueOf(resPtr), reflect.ValueOf(a[i])})
	}
}

/*
通过fx,分裂列表arr。
*/
/*
Split the list arr through FX.
*/
func UnzipWith(fx, arr interface{
    
    }) ([]interface{
    
    }, []interface{
    
    }) {
    
    
	a, f := toArr(arr), reflect.ValueOf(fx)
	r1, r2 := make([]interface{
    
    }, len(a)), make([]interface{
    
    }, len(a))
	for i := range a {
    
    
		outs := f.Call([]reflect.Value{
    
    reflect.ValueOf(a[i])})
		r1[i], r2[i] = outs[0].Interface(), outs[1].Interface()
	}
	return r1, r2
}

/*
通过fx,分裂列表arr,并把结果存入 resPtr1, resPtr2。
*/
/*
Split the list arr through FX and store the results in resptr1 and resptr2.
*/
func UnzipWith2(fx, arr, resPtr1, resPtr2 interface{
    
    }) int {
    
    
	a, b := UnzipWith(fx, arr)
	fill(resPtr1, a...)
	fill(resPtr2, b...)
	return len(a)
}

/*
组合两个函数,返回一个 HalfFunc。
返回的函数会把收到的参数尽量给fx1。
fx1的结果和余下的参数作为fx2的参数。
最终返回fx2的结果。
*/
/*
Combine two functions to return a HalfFunc.
The returned function will give the received parameters to FX1 as much as possible.
The result of FX1 and the remaining parameters are the parameters of FX2.
Finally, the result of FX2 is returned.
*/
func Pipe(fx1, fx2 interface{
    
    }) HalfFunc {
    
    
	f1, f2 := reflect.ValueOf(fx1), reflect.ValueOf(fx2)
	return func(p ...interface{
    
    }) interface{
    
    } {
    
    
		n := f1.Type().NumIn()
		p1, p2 := make([]reflect.Value, n), make([]reflect.Value, len(p)-n)
		Fmap2(reflect.ValueOf, p[:n], &p1)
		Fmap2(reflect.ValueOf, p[n:], &p2)
		rs := Fmap(reflect.Value.Interface, f2.Call(append(f1.Call(p1), p2...)))
		if len(rs) == 1 {
    
    
			return rs[0]
		}
		return rs
	}
}


示例

package fp

import (
	"fmt"
)

func ExampleFmap() {
    
    
	square := func(a int) int {
    
    
		return a * a
	}
	fmt.Printf("%#v\n", Fmap(square, []int{
    
    4, 7}))
	// Output:[]interface {}{16, 49}
}
func ExampleFmap2() {
    
    
	square := func(a int) int {
    
    
		return a * a
	}
	var res [10]int
	n := Fmap2(square, []int{
    
    4, 7}, &res)
	fmt.Printf("%#v\n", res[:n])
	// Output:[]int{16, 49}
}

func ExampleZipWith() {
    
    
	type Student struct {
    
    
		id   int
		name string
	}
	NewStudent := func(id int, name string) Student {
    
    
		return Student{
    
    
			id:   id,
			name: name,
		}
	}
	names, ids := []string{
    
    "Jack", "John"}, []int{
    
    7, 3, 11}
	fmt.Printf("%#v\n", ZipWith(NewStudent, ids, names))
	// Output:
	// []interface {}{fp.Student{id:7, name:"Jack"}, fp.Student{id:3, name:"John"}}
}

func ExampleZipWith2() {
    
    
	type Student struct {
    
    
		id   int
		name string
	}
	NewStudent := func(id int, name string) Student {
    
    
		return Student{
    
    
			id:   id,
			name: name,
		}
	}
	names, ids := []string{
    
    "Jack", "John"}, []int{
    
    7, 3, 11}
	stus := make([]Student, 10)
	n := ZipWith2(NewStudent, ids, names, &stus)
	fmt.Printf("%#v\n", stus[:n])
	// Output:
	// []fp.Student{fp.Student{id:7, name:"Jack"}, fp.Student{id:3, name:"John"}}
}

func ExampleFilter() {
    
    
	ids := []int{
    
    23, 90, 67, 6878, 90, 8}
	fx := func(x int) bool {
    
    
		return x >= 90
	}
	fmt.Printf("%#v\n", Filter(fx, ids))
	// Output:[]interface {}{90, 6878, 90}
}

func ExampleFilter2() {
    
    
	ids := []int{
    
    23, 90, 67, 6878, 90, 8}
	fx := func(x int) bool {
    
    
		return x >= 90
	}
	n := Filter2(fx, ids, &ids)
	fmt.Printf("%#v\n", ids[:n])
	// Output:
	// []int{90, 6878, 90}
}
func ExampleFold() {
    
    
	ids := []int{
    
    -23, 90, 67, 90, 8}
	sum := func(x, y int) int {
    
    
		return x + y
	}
	fmt.Printf("%T : %v\n", Fold(sum, ids), Fold(sum, ids))
	// Output:
	// int : 232
}

func ExampleFold2() {
    
    
	ids := []int{
    
    -23, 90, 67, 90, 8}
	sum := func(x, y int) int {
    
    
		return x + y
	}
	a := 0
	Fold2(sum, ids, &a)
	fmt.Printf("%T : %v\n", a, a)
	// Output:
	// int : 232
}

func ExampleCurrying() {
    
    
	add2 := func(a, b int) int {
    
    
		return a + b
	}
	add1 := Currying(7, add2)
	fmt.Printf("%T : %v\n", add1(2), add1(2))
	// 连续柯里化
	// Compound Currying
	res := Currying(3, add1)
	fmt.Printf("%T : %v\n", res(), res())
	// 固定多个参数
	// Fix multiple parameters
	res2 := Currying([]int{
    
    7, 9}, add2)
	fmt.Printf("%T : %v\n", res2(), res2())
	// 和Fmap复合使用
	// Combined with fmap
	fmt.Printf("%#v\n", Fmap(add1, []int{
    
    4, -3}))
	// 多返回值
	// Multiple return values
	swap2 := func(a, b int) (int, int) {
    
    
		return b, a
	}
	swap1 := Currying(7, swap2)
	swap0 := Currying(3, swap1)
	fmt.Printf("%#v\n", swap0())
	// Output:
	// int : 9
	// int : 10
	// int : 16
	// []interface {}{11, 4}
	// []interface {}{3, 7}
}

func ExamplePipe() {
    
    
	var copyNum = func(a int) (int, int) {
    
    
		return a, a
	}
	add2 := func(a, b int) int {
    
    
		return a + b
	}
	// 部分参数作为第一个函数的参数
	// Some parameters are used as parameters of the first function
	square := func(a int) int {
    
    
		return a * a
	}
	f1 := Pipe(square, add2)
	fmt.Printf("%#v\n", f1(-2, 7))
	// 全部参数作为第一个函数的参数
	// All parameters are the parameters of the first function
	f2 := Pipe(copyNum, func(a, b int) int {
    
    
		return a * b
	})
	fmt.Printf("%#v\n", f2(8))
	// Output:
	// 11
	// 64
}

func ExampleReduce() {
    
    
	box := make([]float64, 0, 10)
	data := []float64{
    
    5, 76, 67, 69, 70, -7, 8}
	collect := func(r *[]float64, b float64) {
    
    
		*r = append(*r, b)
	}
	Reduce(&box, collect, data)
	fmt.Printf("%#v\n", box)
	// Output:
	// []float64{5, 76, 67, 69, 70, -7, 8}
}

func ExampleFlat() {
    
    
	var copyNum = func(a int) (int, int) {
    
    
		return a, a
	}
	arr := Fmap(copyNum, []int{
    
    7, 9})
	fmt.Printf("%#v\n", arr)
	fmt.Printf("%#v\n", Flat(arr))
	// Output:
	// []interface {}{[]interface {}{7, 7}, []interface {}{9, 9}}
	// []interface {}{7, 7, 9, 9}
}
func ExampleFlat2() {
    
    
	var copyNum = func(a int) (int, int) {
    
    
		return a, a
	}
	arr := Fmap(copyNum, []int{
    
    7, 9})
	fmt.Printf("%#v\n", arr)
	var res [6]int
	n := Flat2(arr, &res)
	fmt.Printf("%#v\n", res[:n])
	// Output:
	// []interface {}{[]interface {}{7, 7}, []interface {}{9, 9}}
	// []int{7, 7, 9, 9}
}

func ExampleUnzipWith() {
    
    
	var copyNum = func(a int) (int, int) {
    
    
		return a, a
	}
	a, b := UnzipWith(copyNum, []int{
    
    7, -2, 0})
	fmt.Printf("%#v\n", a)
	fmt.Printf("%#v\n", b)
	// Output:
	// []interface {}{7, -2, 0}
	// []interface {}{7, -2, 0}
}

func ExampleUnzipWith2() {
    
    
	var copyNum = func(a int) (int, int) {
    
    
		return a, a
	}
	var a, b [8]int
	n := UnzipWith2(copyNum, []int{
    
    7, -2, 0}, &a, &b)
	fmt.Printf("%#v\n", a[:n])
	fmt.Printf("%#v\n", b[:n])
	// Output:
	// []int{7, -2, 0}
	// []int{7, -2, 0}
}

猜你喜欢

转载自blog.csdn.net/qq_45256489/article/details/121755362
今日推荐