当前位置: 首页 > news >正文

网站酷站关键词优化排名软件怎么样

网站酷站,关键词优化排名软件怎么样,可以自己建设购物网站,网站的域名和ip地址如何重新解析文章目录 1.什么是泛型?2.有 interface{} 为什么还要有泛型?3.泛型有哪些特性?3.1 类型参数泛型函数泛型类型 3.2 类型约束3.3 类型集3.4 约束元素任意类型约束元素近似约束元素联合约束元素约束中的可比类型 3.5 类型推断 4.实现原理4.1 类型擦除虚方法…

文章目录

  • 1.什么是泛型?
  • 2.有 interface{} 为什么还要有泛型?
  • 3.泛型有哪些特性?
    • 3.1 类型参数
      • 泛型函数
      • 泛型类型
    • 3.2 类型约束
    • 3.3 类型集
    • 3.4 约束元素
      • 任意类型约束元素
      • 近似约束元素
      • 联合约束元素
      • 约束中的可比类型
    • 3.5 类型推断
  • 4.实现原理
    • 4.1 类型擦除
      • 虚方法表
    • 4.2 字典
    • 4.3 单态化
      • 模板(Template)
      • 蜡印(Stenciling)
  • 5.小结
  • 参考wenxian

泛型(Generics)是 Go 语言在较早版本缺失的一个特性,直到 Go 1.18 版本中才引入了泛型。泛型提供了一种更灵活、更通用的方式来编写函数和数据结构,以处理不同类型的数据,而不必针对每种类型编写重复的代码。

1.什么是泛型?

假设我们有一个实现两个数相加的功能函数:

func Add(a int, b int) int {return a + b
}

通过传入 int 类型的 a 和 b,就可以返回 a 和 b 相加后的结果。如果 a 和 b 是 float 类型呢?

如果要解决上述问题,通常有两种解决方法:

(1)增加一个函数。

func AddFloat(a, b float32) float32 {return a + b
}

(2)利用反射,运行时进行类型判断。

func Add(a interface{}, b interface{}) interface{} {switch a.(type) {case int:return a.(int) + b.(int)case float32:return a.(float32) + b.(float32)default:return nil}
}

这两个方法有什么缺点吗?

方法1:会引入新的函数,如果还有其他类型的a,b需要相加的话,就需要再增加更多的函数。

方法2:使用了反射,性能会有影响。

如果不想增加一个新的功能逻辑一模一样的函数,同时也不想使用有性能问题的反射的话。就可以使用泛型。

func Add[T int | float32 | float64](a, b T) T {return a + b
}

泛型是一种编程范式,允许程序员在强类型程序设计语言中编写模板代码来适应任意类型。

通俗一点的描述,泛型是一种相同代码逻辑使用不同类型用的方法,而不需要一遍又一遍地复制和粘贴相同的函数,只是类型发生了变化。

2.有 interface{} 为什么还要有泛型?

虽然 Go 中的空接口 interface{} 允许存储任何类型的值,但它是一种动态类型的机制,并且在使用时需要进行类型断言。相比之下,泛型(Generics)提供了一种静态类型的通用解决方案,使得代码可以在不失去类型安全性的前提下处理多种数据类型。

使用泛型可以带来如下好处:

  • 类型安全

泛型允许开发者在编译时指定代码的通用类型,为类型参数定义一个类型约束,而不需要使用空接口进行运行时类型断言。这提供了更强的类型安全性,因为在编译时就能够发现类型错误。

  • 性能优化

在某些情况下,使用泛型可以带来性能优势。由于泛型代码是在编译时生成的,而不是在运行时进行类型断言,因此它可以更好地进行优化。

  • 代码重用和抽象

泛型允许编写通用的、与具体数据类型无关的代码,从而提高代码的重用性和抽象性。不再需要为每种数据类型都编写相似的代码,避免违反 DRY 原则(Don’t Repeat Yourself)。

3.泛型有哪些特性?

Go 语言泛型实现采用了一种基于类型参数的方式实现更加通用和类型安全的代码,而不是通过接口(像空接口 interface{})和类型断言来实现动态类型的处理。

以下是 Go 泛型实现的基本特性:

3.1 类型参数

Go 的泛型使用类型参数来实现通用性。在定义函数、类型或方法时,可以声明一个或多个类型参数。这些类型参数允许你在代码中引用并操作不同数据类型的对象。

泛型函数

泛型函数允许你编写能够处理不同类型的数据的通用函数,而不必为每种类型编写重复的代码。例如,可以创建一个泛型的排序函数,适用于不同类型的切片。

func Swap[T any](a, b T) (T, T) {return b, a
}

在上面的例子中,T 是一个类型参数,它表示一个占位符,可以代表任意类型。在函数体内,可以使用 T 来表示参数和返回值的类型。

泛型类型

泛型也可以用于创建通用的类型,如泛型切片、泛型映射等。这样可以更灵活地处理不同类型的数据。

type Stack[T any] struct {items []T
}func (s *Stack[T]) Push(item T) {s.items = append(s.items, item)
}func (s *Stack[T]) Pop() T {if len(s.items) == 0 {panic("Stack is empty")}item := s.items[len(s.items)-1]s.items = s.items[:len(s.items)-1]return item
}

上述例子中,Stack 是一个泛型的堆栈数据结构,可以处理任意类型的元素。

3.2 类型约束

为了确保类型安全性,Go 引入了类型约束(type constraints)。

类型约束规定了类型参数必须满足的条件,以便进行合法的操作。例如,可以使用 interface{} 类型进行类型约束,也可以使用特定的接口类型或基本类型。

func Print[T fmt.Stringer](value T) {fmt.Println(value.String())
}

在上述例子中,T 被约束为实现了 fmt.Stringer 接口的类型。

3.3 类型集

类型集(Type Set)表示一堆类型的集合,用来在泛型中约束类型参数的范围。

在 Go1.18 之前,Go 官方对接口的定义是:接口是一个方法集(Method Set)。而 Go1.18 开始将接口的定义正式更改为了类型集。

Go 1.18 引入了一种新的接口语法,可以嵌入其他数据类型,构成类型集合。

type Numeric interface {int | float32 | float64
}

这意味着一个接口不仅可以定义一组方法,还可以定义一组类型。使用 Numeric 接口作为类型约束,意味着值可以是整数或浮点数。

type Node[T Numeric] struct {value T
}

3.4 约束元素

任意类型约束元素

在类型集合中,任意类型均可作为类型参数的约束元素。

// 其中 int 为基础类型
type Integer  interface { int } 

近似约束元素

实际编码时,可能会有很多的类型别名,例如:

type (orderStatus   int32sendStatus    int32receiveStatus int32...
)

Go1.18 中扩展了近似约束元素(Approximation Constraint Element)这个概念,以上述例子来说,即:基础类型为 int32 的类型。语法表现为:

type AnyStatus interface{ ~int32 }

如果我们需要对上述自定义的 status 做一个翻译,就可以使用以下方式:

// 使用定义的类型集
func translateStatus[T AnyStatus](status T) string {switch status {case 1:return "成功"case -1:return "失败"default:return "未知"}
}// 或者不使用类型集
func translateStatus[T ~int32](status T) string {switch status {case 1:return "成功"case -1:return "失败"default:return "未知"}
}

联合约束元素

联合元素,写成一系列由竖线 (|) 分隔的约束元素。

这里给所有有符号的整数类型添加一个通用的求和方法。

type Integer interface {~int | ~int8 | ~int16 | ~int32 | ~int64
}func addInteger[T Integer](a, b T) T {return a + b
}

约束中的可比类型

Go1.18 中内置了一个类型约束 comparable约束,comparable约束的类型集是所有可比较类型的集合。这允许你对该类型的值进行比较操作。

func indexSlice[T comparable](s []T, x T) int {for i, v := range s {if v == x {return i}}return -1
}

3.5 类型推断

在许多情况下,可以使用类型推断来避免必须显式写出部分或全部类型参数。可以利用函数调用使用的实参类型推断出类型参数。

func Print[T any](s T) {fmt.Println(s)
}s := []int{1, 2, 3}// 显示指定参数类型
Print[[]int](s)
// 推断参数类型
Print(s)

4.实现原理

要了解 Go 是如何实现泛型的,首先需要了解一下实现泛型的最常用方法。

4.1 类型擦除

在编译器中实现泛型的另一种方法是类型擦除(Type erasure)。

对 Java 来说统一的数据类型就是 Object,在编译阶段做完类型检查后就将类型信息通过转换成 Object 进行擦除,这样只需要生成一份泛型函数的副本即可。类型擦除保证了泛型函数生成的字节码和非泛型函数的是相同的,也符合 Java 对兼容性的要求。不过类型擦除也给 Java 的泛型带来了很多的限制,比如:

  • 基本类型不支持泛型化

Java 的泛型只能应用于类和对象,无法直接用于基本数据类型(如 int、char)。这导致在使用泛型时需要使用包装类(Wrapper Classes),如 Integer 代替 int。

  • 不能创建参数化类型的数组

例如 List<String>[] array = new ArrayList<String>[10]; 是非法的。这是因为 Java 泛型系统中的数组是具体化的,而泛型是擦除的,二者不兼容。

  • 无法创建参数化类型的实例

不能直接实例化泛型类型。例如,不能使用 new T() 创建泛型类的实例,因为在类型擦除后无法确定 T 的具体类型。

虚方法表

对 Java 来说通过类型擦除结合「虚方法表」(Virtual Method Table)来实现泛型的效果:运行时同样的数据类型 Object,却能调用原始类型的方法。

泛型函数被修改成只接受统一数据类型 Object 作为参数。在调用实际类型对象的方法时,需要找到不通对象的方法。因此,需要一个可以查询方法的内存地址的表格:虚方法表。

虚方法表不仅可以用来实现泛型,还可以用来实现其他类型的多态性,比如 C++ 中的多态。然而,推导这些指针和调用虚拟函数要比直接调用函数慢,而且使用虚方法表会阻止编译器进行优化。

4.2 字典

编译器在编译泛型函数时只生成了一份函数副本,通过新增一个字典参数来供调用方传递类型参数(Type Parameters),这样就可以用一个函数实例支持多种不同的类型参数。这种实现方式称为字典传递(Dictionary passing)。

Go 实现泛型的方式,就是在编译阶段,通过将类型信息以字典的方式传递给泛型函数。当然这个字典不仅包含了类型信息,还包含了此类型的内存操作函数如 make/len/new 等。

同样的 Swift 也通过字典传递了一种名为 Witness table 的数据结构,这种数据结构包含着类型的大小及类型的内存操作函数(移动、复制与释放)。
在这里插入图片描述
Swift 在编译阶段通过在函数入参中以字典的形式把 Witness table 注入,达到了以统一的方式处理任何类型,而不用对类型进行装箱操作。这样一来,Swift就可以实现泛型,而且不需要单态化,虽然在运行时有动态查找的开销,但这种表结构节省了分配内存和缓存不一致性的成本。

4.3 单态化

单态化(Monomorphization)是一个实现泛型的常用方法,编译器为每个被调用的数据类型单独生成一个泛型函数副本,以确保类型安全和最佳性能。

func max[T Numeric](a, b T) T {// ...
}larger := max(3, 5)

由于上面显示的 max 函数是用两个整数调用的,编译器在对代码进行单态化时将为 int 生成一个 max 的副本。

func maxInt(a, b int) int {// ...
}larger := maxInt(3, 5)

单态化针对不同类型创建单独的函数副本,显而易见会增加编译时长,但是可以在编译期针对不同类型进行代码优化,且运行时没有类型相关的判断逻辑,性能较好。

模板(Template)

C++ 通过模板实现泛型类、方法和函数,这导致编译器为每个唯一的类型参数集编译了代码的单独副本。这种方法的一个关键优势是没有运行时性能开销,尽管它以增加编译时间和二进制文件大小为代价。

蜡印(Stenciling)

蜡印其实就是模版,也是一种代码生成技术。但 Go 除了使用字典传递实现装箱外,还采用了 GC Shape Stenciling 的技术。这种看起来很高级的名词简单来说是为了解决蜡印或模版的问题,因为在蜡印的过程中,编译器会为每一个实例化的类型参数生成一套独立的代码,这种会有一个问题,请看下面的例子:

type a int
type b int

虽然 a 和 b 是两个自定义的类型,但它们底层都是 int 类型,但编译器会为这两个类型而生成两套函数的版本(如对 max 泛型函数来说会生成 max_a 与 max_b 两个函数版本)。这是一种浪费,还会生成庞大的二进制文件。

GC Shape 这种技术就是通过对类型的底层内存布局(从内存分配器或垃圾回收器的视角)分组,对拥有相同的内存布局的类型参数进行蜡印,比如指针和接口在内存中总是有相同的布局,编译器将为指针和接口的调用生成同一个泛型函数的副本,这样就可以避免生成大量重复的代码。

Go 实现泛型的方式混合了字典与蜡印技术,对于实例类型的 Shape 相同的情况,只生成一份代码,对于 Shape 相同的类型,使用字典区分类型的不同行为。最终的流程如下:

1.开始编译
2.寻找泛型函数调用
3.使用 GCShape 对类型分组
4.为类型生成实例化的函数
5.创建包含类型信息的字典
6.调用实例化的函数并传递入参与类型字典

总的来说 Go 在 1.18 实现泛型的方式和 Rust 类似,如果感兴趣可以看这篇Rust泛型的文章:透过Rust探索系统的本原:泛型。

5.小结

对静态类型检查的编程语言,实现泛型的方式有很多,如:

  • C++:通过模版实现,相比 C 的宏,模版显然更强大灵活。
  • Java:通过类型擦除的装箱技术结合虚方法表实现,虽然类型擦除导致 Java 的泛型实现不如人意,但这种代价确保了兼容性。
  • Swift:通过字典传入的方式配合 Witness Table 的实现,这种巧妙的方式在编译速度与运行时速度之间取得了不错的平衡。
  • Go:通过字典传入的方式配合 GC Shape 的蜡印技术实现,这种方式在编译速度与运行时速度之间取得了不错的平衡。

虽然看起来方式多样,但实际上只有两种思路:

  • 编译时只生成一份代码,调用方法统一,运行时根据传入的实参类型信息执行相应的操作。
  • 编译时根据不同类型生成不同的代码。

泛型是 Go 语言中一个重要的新增特性,它使得代码更加灵活、清晰,减少了重复代码的编写,提高了代码的可维护性和性能。

在性能讨论中经常被忽略的是,所有这些好处和成本只涉及到函数的调用。通常情况下,大部分的执行时间在函数内部。调用方法的开销可能不会成为性能瓶颈,所以要考虑先优化函数实现,再考虑调用开销。

Go 也在不断的改进自己实现泛型的机制,所以此文会存在一些时效性的问题。


参考wenxian

An Introduction To Generics
Type Parameters Proposal
泛型设计 - | Go 语言设计哲学- 煎鱼
golang拾遗:为什么我们需要泛型- apocelipes
简单易懂的Go 泛型使用和实现原理介绍- 万俊峰Kevin
编程语言是如何实现泛型的 - BMPI
Go泛型是怎么实现的? - 鸟窝

http://www.hengruixuexiao.com/news/14647.html

相关文章:

  • 涂料网站模板百度seo公司哪家强一点
  • 登不上学校的网站该怎么做百度top排行榜
  • 网站搭建中114514青岛招聘seo
  • 静态网站 服务器网络安全培训
  • dedecms做的网站手机上看图片变形百度排名软件
  • redis做网站统计苏州网站建设开发公司
  • 湖南智慧住建云怎样下载优化大师
  • 网站建设毕业答辩ppt市场调研报告3000字范文
  • 做产品网站多少钱平台优化
  • 一键提交各大收录win10优化工具
  • 东莞常平建设局网站怎么自己开网站
  • 刷赞网站空间免费网络广告案例以及分析
  • 七牛云wordpress缓存附件seo教程培训班
  • 网站建设有什么岗位茶叶营销策划方案
  • 网站建设中html模板百度推广开户代理
  • 用什么软件做网站好处百度推广优化技巧
  • 专业网站开发技术汕头网站快速优化排名
  • 北京好的网站设计公司小广告网页
  • 楚雄市住房和城乡建设局门户网站哈尔滨百度网站快速优化
  • 济南建网站网站访问量统计工具
  • 做火影忍者网站的格式品牌营销策划ppt
  • 晋城做网站网络销售 市场推广
  • 什么网站上做任务赚钱餐饮最有效的营销方案
  • 商标注册代理seo引擎搜索网站
  • dreamweaver网站建设教程推广宣传
  • 东莞 建网站seo管理平台
  • 深圳旅游公司网站十大室内设计网站
  • 滁州做网站电话号码论坛seo网站
  • 上海做网站建设的公司排名seo一个月赚多少钱
  • 帮企业做网站前景怎么样关键词排名是什么意思