因势而变,因时而动,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang泛型(generic)的使用EP15

事实上,泛型才是Go lang1.18最具特色的所在,但为什么我们一定要拖到后面才去探讨泛型?类比的话,我们可以想象一下给小学一年级的学生讲王勃的千古名篇《滕王阁序》,小学生有多大的概率可以理解作者的青云之志以及壮志难酬的愤懑心情?恐怕很难罢,是的,如果对Go lang的强类型语法没有一段时间的体验期,就很难理解泛型这种”反”静态语言概念。

基本概念

什么是泛型?泛型泛型,顾名思义,泛用的类型,说白了,就是在静态类型语言环境使用动态类型语言的特性:


package main

import (
    "fmt"
)

func sum(a string, b string) string {

    s := a + b
    return s
}

func main() {

    a := "1"
    b := "2"

    fmt.Println(sum(a, b))
}

比方说有一个函数可以实现两个字符串合并,参数声明了字符串,也就不支持其他的数据类型,但如果逻辑上差不多,需要两个整形求和的函数怎么办?那就得再写一个差不多的函数,这样就影响了代码逻辑的复用性。

相同逻辑下可以针对不同的数据类型进行泛用,这就是泛型的意义所在。

泛型声明

Go lang中的泛型使用 [] 来申明类型范围:

func sum[v int | float64 | string](a v, b v) v {

    s := a + b

    return s
}

如果是多个数据类型,可以使用|分隔,这里定义了一个泛型变量v,可以是整形、浮点以及字符串:

package main

import (
    "fmt"
)

func sum[v int | float64 | string](a v, b v) v {

    s := a + b

    return s
}

func main() {

    a := "1"
    b := "2"

    fmt.Println(sum(a, b))
}

程序返回:

12

注意,由于参数的类型未定,所以返回值也必须是泛型类型,现在动态的把参数改为整形:

package main

import (
    "fmt"
)

func sum[v int | float64 | string](a v, b v) v {

    s := a + b

    return s
}

func main() {

    a := 1
    b := 2

    fmt.Println(sum(a, b))
}

返回值也因为参数类型的改变而改变:

藉此,我们就声明了一个可以”泛用”的函数。

高阶应用

事实上,泛型的出现并非可以丰富函数的声明和构建,更多的,是战略层面上的多样化选择,比如容器内的类型,进而言之,队列:

type Queue[T interface{}] struct {
    elements []T
}

// 将数据放入队列尾部
func (q *Queue[T]) Put(value T) {
    q.elements = append(q.elements, value)
}

// 从队列头部取出并从头部删除对应数据
func (q *Queue[T]) Pop() (T, bool) {
    var value T
    if len(q.elements) == 0 {
        return value, true
    }

    value = q.elements[0]
    q.elements = q.elements[1:]
    return value, len(q.elements) == 0
}

这里结构体的类型约束使用了空接口,代表的意思是所有类型都可以用来实例化泛型类型,同时基于泛型结构体,我们定义两个方法,分别是:入队和出队。

因为这个队列是泛型队列,所以队内元素的类型可以在实现结构体接口时进行定义:

package main

import (
    "fmt"
)

type Queue[T interface{}] struct {
    elements []T
}

// 将数据放入队列尾部
func (q *Queue[T]) Put(value T) {
    q.elements = append(q.elements, value)
}

// 从队列头部取出并从头部删除对应数据
func (q *Queue[T]) Pop() (T, bool) {
    var value T
    if len(q.elements) == 0 {
        return value, true
    }

    value = q.elements[0]
    q.elements = q.elements[1:]
    return value, len(q.elements) == 0
}

func main() {

    var q1 Queue[int] // 可存放int类型数据的队列
    q1.Put(1)
    q1.Put(2)
    q1.Put(3)
    fmt.Println(q1)

    var q2 Queue[string] // 可存放string类型数据的队列
    q2.Put("A")
    q2.Put("B")
    q2.Put("C")

    fmt.Println(q2)
}

程序返回:

{[1 2 3]}
{[A B C]}

匿名函数和方法暂不支持泛型

Golang中,我们经常会使用匿名函数:

package main

import (
    "fmt"
)

func main() {

    fn := func(a, b int) int {
        return a + b
    } // 定义了一个匿名函数并赋值给 fn

    fmt.Println(fn(1, 2)) // 输出: 3
}

程序返回:

大体上,和Python的lambda表达式类似,如果封装的逻辑相对简单或者和上下游逻辑连贯性较强,那么,在不影响代码可读性的前提下,我们就没必要单独声明一个函数,而是选择匿名函数。

但1.18版本中,匿名函数并不支持参数为泛型,因为匿名函数不能自己定义类型形参:

fnGeneric := func[T int | string](a, b T) T {
        return a + b
}

程序报错:

./hello.go:9:19: syntax error: function literal must have no type parameters

但匿名函数可以使用已经被合法定义的泛型类型:

package main

import (
    "fmt"
)

func test[T int | float32 | float64](a, b T) {

    // 匿名函数可使用已经定义好的类型形参
    fn2 := func(i T, j T) T {
        return i + j
    }

    fmt.Println(fn2(a, b))
}

func main() {

    test(1, 2)

}

程序返回:

也就是说,匿名函数可以使用父级函数定义好的泛型类型参数,这意味着,在泛型函数内,我们可以通过匿名函数对逻辑进行二次封装。

同样地,1.18版本中的方法也不支持泛型:

type A struct {
}

// 不支持泛型方法
func (receiver A) Add[T int | float32 | float64](a T, b T) T {
    return a + b
}

程序报错:

syntax error: method must have no type parameters

但是和匿名函数类型,因为receiver支持泛型,所以我们可以声明结构体内receiver的参数为泛型类型:

package main

import "fmt"

type A[T int | float32 | float64] struct {
}

// 方法可以使用类型定义中的形参 T
func (receiver A[T]) Add(a T, b T) T {
    return a + b
}

func main() {

    var a A[int]
    res := a.Add(1, 2)

    fmt.Println(res)

}

程序返回:

因为receiver声明了泛型参数,我们为结构体A绑定的方法也就可以直接使用声明好的泛型类型,和匿名函数直接用父级泛型是一个意思。

事实上,静态语言在设计上基本都有泛型的概念,这并不是自我矛盾,对应的,在动态语言Python中为函数声明形参时,我们其实也可以指定具体的参数类型或者返回值类型,正所谓无招胜有招,真正的高手,可以脱离语言类型的桎梏,达到一种无我无众生的境界,比如,在固有思维模式中,降龙十八掌是一种至刚至猛的武功,威力无穷,无坚不摧,但郭大侠后期再使用这门神功时,降龙十八掌的劲力忽强忽弱,忽吞忽吐,从至刚之中竟生出至柔的妙用,那已是洪七公当年所领悟不到的境界,所以,刚柔并济、虚中有实、实中有虚、虚实相生才是泛型使用的最高境界。

Original: https://www.cnblogs.com/v3ucn/p/16640558.html
Author: 刘悦的技术博客
Title: 因势而变,因时而动,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang泛型(generic)的使用EP15

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/803766/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • cmd基础操作

    cmd重要性 cmd是command的缩写,一直伴随着windows操作系统。有时称为:控制台窗口,cmd窗口,黑窗口,命令行窗口等。 其实,在unix系统,Linux,MacOS…

    Python 2023年6月6日
    074
  • python DataFrame的stack()方法,unstack()方法,pivot()方法

    文章目录 1.stack() 2. unstack() 3. pivot() ; 1.stack() stack()用于将列索引转换为最内层的行索引,这样叙述比较抽象,看示例就容易…

    Python 2023年8月7日
    034
  • Python控制台输出俄罗斯方块移动和旋转

    今天填一个坑,俄罗斯方块!! 俄罗斯方块的移动不难实现,但是老师给定了字典数据结构,旋转就不太容易实现,当然也可以用list嵌套list来做。今天把旋转做出来了,刚好整理一下,把之…

    Python 2023年9月21日
    069
  • docker命令之docker build

    明天要讲docker file的公开课,正好借此机会,整理下docker 命令的专题 docker build [OPTIONS] PATH | URL | – 通过dockerf…

    Python 2023年6月12日
    0110
  • 机器学习番外篇:NumPy的基本知识

    NumPy 一、创建数组 二、数组的索引与切片 三、数组的基本运算 四、数组维度变换 五、数组合并与切分 一、创建数组 导入 numpy 模块 import numpy as np…

    Python 2023年8月26日
    029
  • 【疫情动态条形图】用Python开发全球疫情排名动态条形图bar_chart_race

    一、开发背景 二、讲解代码 2.1 获取数据 2.2 数据清洗 2.3 可视化开发 三、附完整源码 一、开发背景 你好,我是 @马哥python说 ,这是我用Python开发的全球…

    Python 2023年10月20日
    038
  • ChatGPT解答一道1-2年级水平的袋鼠数学竞赛题,答案竟出乎意料

    最近一段时间ChatGPT风光无限,可真是火出了圈内圈外。ChatGPT是一个人工智能聊天机器人程序,由OpenAI基于GPT-3.5架构的大型语言模型进行训练,通过人机交互的问答…

    Python 2023年11月4日
    043
  • Django初识下

    Http协议是超文本传输语言 get 获取服务端数据post 向服务端提交数据put 向服务端上传数据delete 删除服务端的资源 状态码 状态码英文标示 意义200 OK 请求…

    Python 2023年8月5日
    034
  • python接收前端post数据_python实现通过flask和前端进行数据收发

    python代码: –– coding: utf-8 –– from flask import Flask,jsonify,rend…

    Python 2023年8月14日
    069
  • python虚拟环境的使用

    一、python虚拟环境介绍 虚拟环境(virtual environment),它是一个虚拟化,从电脑独立开辟出来的环境。 通俗的来讲,虚拟环境就是借助虚拟机来把一部分内容独立出…

    Python 2023年8月5日
    049
  • 群智能算法 第5关:动手实现旅行商问题

    本关任务:使用 python 实现遗传算法解决 TSP 问题。 为了完成本关任务,你需要掌握:1.使用遗传算法解决 TSP 问题。 使用遗传算法解决TSP问题。 蚁群算法对于参数的…

    Python 2023年8月26日
    058
  • 前端面试题合集-第一篇

    前端面试题合集-第一篇 🔔每周不定时更新! ⛄️不要让自己失去竞争力! ☀️哪里都不是避风港,保持竞争力! 1. CSS选择器的优先级 !important> &#x…

    Python 2023年11月6日
    030
  • ECG:机器学习之预处理算法与R波定位算法(附部分代码)

    ECG:机器学习之预处理算法与R波定位算法(附部分代码) 目前网上有关ECG信号处理的文章越来越多,说明这一块做的人也越来越多,但是网上的资源却很有限,而且很多资源都有一定的缺陷且…

    Python 2023年10月27日
    0106
  • Part 9:Pandas 的字符串处理操作

    Pandas字符串处理 前面我们已经使用了字符串的处理函数:df[“bWendu”].str.replace(“℃”,”…

    Python 2023年8月8日
    054
  • Python——import导入模块

    Python导入第三方库或者使用标准库。 如使用数学函数,import math ,如余弦函数cos,绝对值函数fabs();import numpy as np,解决矩阵数组问题…

    Python 2023年8月26日
    057
  • 无助和愤怒都有

    要说曾经有多爱,现在就有多恨。 这几天很 焦躁,今天冷静下一想,不是因为别的,焦躁的主要原因是因为那种无助感。 我记得之前,家里有事,我去当地法院,我是被迫和解的,和解的过程中,法…

    Python 2023年6月15日
    074
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球