在本文中,我们将学习函数式编程中的三个基本操作:Map、Reduce 和 Filter。这些操作使我们能够方便灵活地执行数据处理,这是我们大多数程序中的常见任务。特别是对于需要统计分析的场景,地图、化简和过滤是功能强大的工具。让我们深入了解一些示例以更好地理解这些概念:
地图示例:
在下面的代码中,我们有两个 Map 函数,它们采用两个参数:
- 一个字符串数组 []string,表示要处理的数据。
- 函数 func(s string) 字符串或 func(s string) int。
goCopy code<code>func MapStrToStr(arr []string, fn func(s string) string) []string {
var newArray = []string{}
for _, it := range arr {
newArray = append(newArray, fn(it))
}
return newArray
}
func MapStrToInt(arr []string, fn func(s string) int) []int {
var newArray = []int{}
for _, it := range arr {
newArray = append(newArray, fn(it))
}
return newArray
}
</code>
这两个 Map 函数的工作方式类似,即遍历第一个参数(数组)并将第二个参数的函数应用于每个元素,然后将结果收集到新数组中。
我们可以按如下方式使用这些函数:
goCopy code<code>var list = []string{"Hao", "Chen", "MegaEase"}
// Convert strings to uppercase
x := MapStrToStr(list, func(s string) string {
return strings.ToUpper(s)
})
fmt.Printf("%v\n", x)
// ["HAO", "CHEN", "MEGAEASE"]
// Get the length of each string
y := MapStrToInt(list, func(s string) int {
return len(s)
})
fmt.Printf("%v\n", y)
// [3, 4, 8]
</code>
如您所见,我们将不同的函数传递给 MapStrToStr() 和 MapStrToInt() 以获得不同的结果,展示了 Map 操作的灵活性。
减少示例:
Reduce函数采用两个参数:
- 字符串数组 []string 或 []int,表示要处理的数据。
- 函数函数 func(s string) int。
goCopy code<code>func Reduce(arr []string, fn func(s string) int) int {
sum := 0
for _, it := range arr {
sum += fn(it)
}
return sum
}
</code>
该函数循环访问数组并将提供的函数应用于每个元素,从而将数组缩减为单个值。
用法示例:
goCopy code<code>var list = []string{"Hao", "Chen", "MegaEase"}
x := Reduce(list, func(s string) int {
return len(s)
})
fmt.Printf("%v\n", x)
// 15
</code>
筛选器示例:
Filter 函数采用两个参数:
- 一个整数数组 []int,表示要处理的数据。
- 函数 func(n int) bool。
goCopy code<code>func Filter(arr []int, fn func(n int) bool) []int {
var newArray = []int{}
for _, it := range arr {
if fn(it) {
newArray = append(newArray, it)
}
}
return newArray
}
</code>
该函数循环访问数组并将提供的函数应用于每个元素,仅保留函数返回 true 的元素。
用法示例:
goCopy code<code>var intset = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Keep odd numbers
out := Filter(intset, func(n int) bool {
return n%2 == 1
})
fmt.Printf("%v\n", out)
// [1, 3, 5, 7, 9]
// Keep numbers greater than 5
out = Filter(intset, func(n int) bool {
return n > 5
})
fmt.Printf("%v\n", out)
// [6, 7, 8, 9, 10]
</code>
业务示例:
通过使用上面的 Map、Reduce 和 Filter 函数,我们可以轻松地将代码中的业务逻辑与控制逻辑分开。让我们考虑一个包含员工信息的示例:
goCopy code<code>type Employee struct {
Name string
Age int
Vacation int
Salary int
}
var list = []Employee{
{"Hao", 44, 0, 8000},
{"Bob", 34, 10, 5000},
{"Alice", 23, 5, 9000},
{"Jack", 26, 0, 4000},
{"Tom", 48, 9, 7500},
{"Marry", 29, 0, 6000},
{"Mike", 32, 8, 4000},
}
func EmployeeCountIf(list []Employee, fn func(e *Employee) bool) int {
count := 0
for i, _ := range list {
if fn(&list[i]) {
count += 1
}
}
return count
}
func EmployeeFilterIn(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
}
func EmployeeSumIf(list []Employee, fn func(e *Employee) int) int {
var sum = 0
for i, _ := range list {
sum += fn(&list[i])
}
return sum
}
</code>
使用这些函数,我们可以对员工数据执行各种统计分析:
goCopy code<code>// Count employees above 40 years old
old := EmployeeCountIf(list, func(e *Employee) bool {
return e.Age > 40
})
fmt.Printf("Old employees: %d\n", old)
// Old employees: 2
// Count employees with a salary above 6000
high_pay := EmployeeCountIf(list, func(e *Employee) bool {
return e.Salary >= 6000
})
fmt.Printf("High salary employees: %d\n", high_pay)
// High salary employees: 4
// Filter employees with no vacation days
no_vacation := EmployeeFilterIn(list, func(e *Employee) bool {
return e.Vacation == 0
})
fmt.Printf("Employees with no vacation: %v\n", no_vacation)
// Employees with no vacation: [{Hao 44 0 8000} {Jack 26 0 4000} {Marry 29 0 6000}]
// Calculate the total salary of all employees
total_pay := EmployeeSumIf(list, func(e *Employee) int {
return e.Salary
})
fmt.Printf("Total salary: %d\n", total_pay)
// Total salary: 43500
// Calculate the total salary of employees under 30 years old
younger_pay := EmployeeSumIf(list, func(e *Employee) int {
if e.Age < 30 {
return e.Salary
}
return 0
})
</code>
通用地图缩减:
目前,Go 并不直接支持真正的泛型,但我们可以使用接口和反射实现一个简单的版本。下面是一个通用的地图函数:
goCopy code<code>func Map(data interface{}, fn interface{}) []interface{} {
vfn := reflect.ValueOf(fn)
vdata := reflect.ValueOf(data)
result := make([]interface{}, vdata.Len())
for i := 0; i < vdata.Len(); i++ {
result[i] = vfn.Call([]reflect.Value{vdata.Index(i)})[0].Interface()
}
return result
}
</code>
该函数允许我们通过传递适当的函数对不同类型的数据使用相同的逻辑。
goCopy code<code>square := func(x int) int {
return x * x
}
nums := []int{1, 2, 3, 4}
squared_arr := Map(nums, square)
fmt.Println(squared_arr)
// [1 4 9 16]
upcase := func(s string) string {
return strings.ToUpper(s)
}
strs := []string{"Hao", "Chen", "MegaEase"}
upstrs := Map(strs, upcase)
fmt.Println(upstrs)
// [HAO CHEN MEGAEASE]
</code>
请注意,使用反射可能会影响性能,因此不适合高性能方案。对于此类情况,您可能需要使用替代方法,例如代码生成。此外,Go 缺乏真正的泛型有时会使代码更加冗长,但它仍然提供了一种强大而灵活的方式来处理各种数据类型。
总之,Map、Reduce和Filter 是功能强大的函数式编程操作,可以大大简化数据处理并将业务逻辑与控制逻辑分离。理解这些概念将使你的 Go 程序更加简洁和易于维护。
(注意:本文不涉及使用强大的类型检查实现Reduce和Filter。实现可靠的类型检查需要额外的注意事项和复杂性,这超出了本文的范围。)
暂无评论内容