Golang函数式编程深入分析实例
梦想画家 人气:0定义集合功能函数
首先定义用于测试的结构体WorkWith
:
// WorkWith is the struct we'll // be implementing collections for type WorkWith struct { Data string Version int }
针对该结构体定义filter和map函数:
// 基于判断函数过滤集合,返回符合条件的集合元素 func Filter(ws []WorkWith, f func(w WorkWith) bool) []WorkWith { // depending on results, smaller size for result // is len == 0 result := make([]WorkWith, 0) for _, w := range ws { if f(w) { result = append(result, w) } } return result } // 基于转换函数转换集合元素,返回集合的元素为转换后的元素 func Map(ws []WorkWith, f func(w WorkWith) WorkWith) []WorkWith { // the result should always be the same // length result := make([]WorkWith, len(ws)) for pos, w := range ws { newW := f(w) result[pos] = newW } return result }
实现具体功能函数
import "strings" // LowerCaseData does a ToLower to the // Data string of a WorkWith func LowerCaseData(w WorkWith) WorkWith { w.Data = strings.ToLower(w.Data) return w } // IncrementVersion increments a WorkWiths // Version func IncrementVersion(w WorkWith) WorkWith { w.Version++ return w } // OldVersion returns a closures // that validates the version is greater than // the specified amount func OldVersion(v int) func(w WorkWith) bool { return func(w WorkWith) bool { return w.Version >= v } }
上面定义了三个函数,LowerCaseData修改WorkWith中Data值为小写形式,IncrementVersion让WorkWith中版本增加1,OldVersion基于参数过滤版本。
测试集合功能
定义测试用例文件:
import ( "fmt" "testing" ) func TestMap(t *testing.T) { ws := []WorkWith{ {"Example", 1}, {"Example 2", 2}, } fmt.Printf("Initial list: %#v\n", ws) // first lower case the list ws = Map(ws, LowerCaseData) fmt.Printf("After LowerCaseData Map: %#v\n", ws) // next increment all versions ws = Map(ws, IncrementVersion) fmt.Printf("After IncrementVersion Map: %#v\n", ws) // lastly remove all versions older than 3 ws = Filter(ws, OldVersion(3)) fmt.Printf("After OldVersion Filter: %#v\n", ws) }
运行 go test . -v
输出结果如下:
Initial list: []collections.WorkWith{collections.WorkWith{Data:"Example", Version:1}, collections.WorkWith{Data:"Example 2", Version:2}}
After LowerCaseData Map: []collections.WorkWith{collections.WorkWith{Data:"example", Version:1}, collections.WorkWith{Data:"example 2", Version:2}}
After IncrementVersion Map: []collections.WorkWith{collections.WorkWith{Data:"example", Version:2}, collections.WorkWith{Data:"example 2", Version:3}}
After OldVersion Filter: []collections.WorkWith{collections.WorkWith{Data:"example 2", Version:3}}
上面示例中,我们注意到函数都没有返回任何error对象,这遵循函数式编程思想,尽可能让函数纯粹:不修改原集合元素,即对原集合无副作用,而是生成新的集合。如果需要对集合应用多个功能,那么这种模式能够省去很多麻烦,并且测试也很简单。我们还可以将映射和过滤器链接在一起,让代码更简洁可读。
ws := []WorkWith{ {"Example", 1}, {"Example 2", 2}, } fmt.Printf("Initial list: %#v\n", ws) result := Filter(Map(Map(ws, LowerCaseData), IncrementVersion), OldVersion(3)) fmt.Printf("After OldVersion Filter: %#v\n", result)
如果功能函数定义为集合类型的方法,并返回集合类型,则上述代码会更优雅。
泛型实现
上面代码仅能在特定类型上使用,我们自然想实现泛型函数,下面通过一个简单示例进行说明:
func map2[T, U any](data []T, f func(T) U) []U { res := make([]U, 0, len(data)) for _, e := range data { res = append(res, f(e)) } return res }
该函数接收类型T,转换后返回类型U,当然两者类型也可以一样。下面测试函数功能:
// 字符串转大写 words := []string{"war", "cup", "water", "tree", "storm"} result := map2(words, func(s string) string { return strings.ToUpper(s) }) fmt.Println(result) // 生成原集合元素的平方集合 fmt.Println("-------------------") numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} squares := map2(numbers, func(n int) int { return n * n }) fmt.Println(squares) // 数值转为字符串 fmt.Println("-------------------") as_strings := map2(numbers, func(n int) string { return strconv.Itoa(n) }) fmt.Printf("%q", as_strings)
加载全部内容