Golang函数式编程深入分析实例

 更新时间:2023年01月10日 11:00:24   作者:梦想画家  
习惯与函数式编程语言的开发者,会认为for循环和if判断语句是冗长的代码,通过使用map和filter处理集合元素让代码更可读。本文介绍Go闭包实现集合转换和过滤功能

定义集合功能函数

首先定义用于测试的结构体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)

到此这篇关于Golang函数式编程深入分析实例的文章就介绍到这了,更多相关Go函数式编程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言学习教程之声明语法(译)

    Go语言学习教程之声明语法(译)

    Golang 就是类C的语法,下面这篇文章主要给大家介绍了关于Go语言学习教程之声明语法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-11-11
  • Go语言并发范式之future模式详解

    Go语言并发范式之future模式详解

    编程中经常遇到在一个流程中需要调用多个子调用的情况,此时就可以使用Go并发编程中的future模式,下面小编就来和大家聊聊future模式的具体使用,需要的可以参考一下
    2023-06-06
  • Go语言ORM框架构造查询条件示例详解

    Go语言ORM框架构造查询条件示例详解

    这篇文章主要为大家介绍了Go语言ORM框架构造查询条件示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 一些关于Go程序错误处理的相关建议

    一些关于Go程序错误处理的相关建议

    错误处理在每个语言中都是一项重要内容,众所周知,通常写程序时遇到的分为异常与错误两种,Golang中也不例外,这篇文章主要给大家介绍了一些关于Go程序错误处理的相关建议,需要的朋友可以参考下
    2021-09-09
  • Golang初始化MySQL数据库方法浅析

    Golang初始化MySQL数据库方法浅析

    这篇文章主要介绍了Golang初始化MySQL数据库的方法,数据库的建立第一步即要初始化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-05-05
  • Go语言学习之goroutine详解

    Go语言学习之goroutine详解

    Goroutine是建立在线程之上的轻量级的抽象。它允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法,这篇文章主要介绍了Go语言学习之goroutine的相关知识,需要的朋友可以参考下
    2020-02-02
  • golang语言如何将interface转为int, string,slice,struct等类型

    golang语言如何将interface转为int, string,slice,struct等类型

    这篇文章主要介绍了golang语言如何将interface转为int, string,slice,struct等类型,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Go设计模式之模板方法模式讲解和代码示例

    Go设计模式之模板方法模式讲解和代码示例

    模版方法是一种行为设计模式, 它在基类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤,本文将通过代码示例给大家详细的介绍一下Go模板方法模式,需要的朋友可以参考下
    2023-08-08
  • Golang之sync.Pool对象池对象重用机制总结

    Golang之sync.Pool对象池对象重用机制总结

    这篇文章主要对Golang的sync.Pool对象池对象重用机制做了一个总结,文中有相关的代码示例和图解,具有一定的参考价值,需要的朋友可以参考下
    2023-07-07
  • Golang使用WebSocket通信的实现

    Golang使用WebSocket通信的实现

    这篇文章主要介绍了Golang使用WebSocket通信的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02

最新评论