Go-知识泛型
Go-知识泛型
- 1. 认识泛型
- 1.1 不使用泛型
- 1.2 使用泛型
- 2. 泛型的特点
- 2.1 函数泛化
- 2.2 类型泛化
- 3. 类型约束
- 3.1 类型集合
- 3.2 interface 类型集合
- 3.2.1 内置interface类型集合
- 3.2.2 自定义interface类型集合
- 3.2.2.1 任意类型元素
- 3.2.2.2 近似类型元素
- 3.2.2.3 联合类型元素
- 3.2.3 interface类型集合运算
- 3.2.4 基于操作的类型集合
- 4. 小例子
- 4.1 map.Keys 获取map的全部key
- 4.2 Set
- 4.3 排序 Sort
- 5. 总结
泛型是程序设计语言的一种风格或范式,允许程序员在编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
Java,C++等多种编程语言都支持泛型,Go语言从1.18版本起也开始支持泛型。
1. 认识泛型
1.1 不使用泛型
实现两个函数对map的value值进行累加,一个是int64类型的值,一个是float64类型的值
func SumInt64(m map[string]int64) int64 {var sum int64for _, v := range m {sum += v}return sum
}func SumFloat64(m map[string]float64) float64 {var sum float64for _, v := range m {sum += v}return sum
}
接着使用两个函数
func TestSum(t *testing.T) {ints := map[string]int64{"one": 234,"two": 6755,"three": 78675,}floats := map[string]float64{"one": 123.456,"two": 7865.9658,"three": 87906.865,}t.Logf("sum ints : %v , floats : %v", SumInt64(ints), SumFloat64(floats))
}
执行如下:
实现同样的功能,就因为处理的数据类型不一样,就需要为每种类型编写类似的重复代码。
1.2 使用泛型
泛型函数,就是吧函数的参数和返回值“泛化”,使逻辑通用。 通用并不是对所有类型都使用,所以在声明泛型函数时,需要声明适用的“参数类型”(类型约束).
func SumValue[K comparable, V int64 | float64](m map[K]V) V {var sum Vfor _, v := range m {sum += v}return sum
}
SumValue泛型函数通过[K comparable, V int64|fload64]
声明了两个类型参数K,V,供函数参数和返回值使用。
类型参数K的类型必须为comparable类型,因为被用作map的key值,在Go语言中map的key值必须是可比较的类型。
类型参数V的类型可以是int64或float64,在声明时使用|
组合支持的类型。
普通参数m 表示一个泛化的map,相应的返回值也是一个泛化的类型。
使用:
func TestSumValue(t *testing.T) {ints := map[string]int64{"one": 234,"two": 6755,"three": 78675,}floats := map[string]float64{"one": 123.456,"two": 7865.9658,"three": 87906.865,}t.Logf("sum ints : %v , floats : %v", SumValue(ints), SumValue[string, float64](floats))
}
执行结果
在调用泛型函数的地方,编译器会将泛型函数实例化,即使用真实的类型来替换类型参数,在调用时有两种方式:
- 隐式调用: 调用泛型函数时使用缺省类型参数,让编译器根据实际的参数进行推导。(SumValue(ints))
- 显示调用: 调用泛型函数时显式的指明类型参数。(SumValuestring, float64)
需要注意的是,编译器之所以能够推导出参数类型是因为函数存在入参,编译器通过传入的变量和函数的参数声明可以推导出参数类型,但对于没有函数参数的泛型函数来说,编译器
无法进行推导,也就无法实例化泛型函数,此时必须显式地调用并指定类型参数。
比如:
就无法推导出来返回值的类型了
而且显式调用,必须全部指定,不能指定部分类型
2. 泛型的特点
泛型的英文表述为generic,即一般化,泛化,具体来讲就是函数和类型的泛化。在泛型被引入之前一个函数所能接收的参数类型及
所能处理的数据类型是确定的,但泛型函数却能接受和处理多种类型,对于类型也是同样的道理。
泛型主要包含三方面的内容:
- 函数的泛化
- 类型的泛化
- 接口的扩展
2.1 函数泛化
为了支持泛型,Go语言函数扩展为可以接受一个使用方括弧表示的类型参数;
func SumValue1[K comparable, V int64 | float64, T int64 | float64](m map[K]V) T
同普通的函数参数类似,类型参数中的每个参数也有一个类型,比如参数K,V的类型分别为 comparable(伴随泛型而引入的内置interface类型,表示可比类型)
和int64|float64(表示int64或float64)。
函数中的类型参数是可选的,没有类型参数的函数是传统的函数,带有类型参数的函数则是泛型函数。
即便Go 1.18 引入了泛型并且扩展了函数,但仍然保持兼容(在这里小小的谴责一下 python 和 scala )。
类型参数中的类型正式的名称是类型约束,用来约束类型的范围。 上面额函数可以接受多种类型的map为参数,考虑到所有的map的key的类型都是comparable类型,
那么只要一个map的值类型是int64或float64就能调用泛型函数。
比如: map[int]int64,map[int64]int64,map[float64]float64,...
2.2 类型泛化
泛型同样扩展了类型的表示方法,允许在创建自定义类型时也能接受一个使用方括弧表示的类型参数。
type arr[T int|int64] []T
声明arr类型,可以容纳int或者int64的切片。
这种声明中带有类型参数的类型被称为泛型类型。
泛型类型必须通过类型参数实例化后才可以使用。
var arrInt arr[int]
var arrInt64 arr[int64]
但是当实例化允许范围之外的类型时,会编译异常
在实例化一个泛型类型时,必须指定类型参数(编译器无法自动推导)
泛型类型同普通类型一样,同样允许定义方法,但是其类型必须带上类型参数:
func (a *arr[T]) add(x T) {*a = append(*a, x)
}
func TestArrAdd(t *testing.T) {var a arr[int]a.add(3)a.add(4)t.Logf("res : %v", a)
}
执行结果
未泛型类型定义方法时,必须指定类型参数,但参数名可以与泛型类型声明不同。
func (a *arr[X]) add1(x X) {*a = append(*a, x)
}
定义时使用T,但是在使用的时候,可以与声明时的名字不同。
如果方法体中并未使用类型参数,甚至可以使用_
省略
func (a *arr[_]) add2(x _) {*a = append(*a, x)
}
但是不管是换个名字还是使用_
并没有任何好处,反而降低了可读性。
3. 类型约束
无论函数和类型如何泛化,都需要类型参数来限定其泛化的范围,类型参数使用类型的集合表示范围。
3.1 类型集合
func SumValue1[K comparable, V int64 | float64, T int64 | float64](m map[K]V) T
该函数的类型参数中K的类型限定为comparable,V 的类型限定为int64或float64。
comparable是interface类型,int64|float64是组合类型,都代表一个类型集合,用于约束泛化的范围。
使用|
来组合多个类型,从而形成一个类型集合。
3.2 interface 类型集合
在泛型特性被引入之前,interface仅表示一个方法集合,实现了该方法结合的所有类型都可认为实现了这个interface。
在泛型的设计中,对interface进行了扩展,interface将不在仅仅表示方法集合,它还可用于表示类型集合,同理,集合内所有类型都可认为实现了这个interface。
3.2.1 内置interface类型集合
comparable就是跟随泛型被引入的内置interface类型
除了comparable还有any。
comparable表示可比较类型的集合,仅能用于类型参数中。
any不仅在类型参数中表示任意类型的集合,还可以在非泛型场景中作为interface{}的别名使用。
3.2.2 自定义interface类型集合
除了内置的comparable和any两种类型可作为类型约束使用,用户还可以使用interface来定义类型集合。
在泛型之前,interface类型中仅允许包含方法或内嵌interface两种元素,引入泛型后,interface类型将允许使用另外三种元素以表示一个类型集合:
- 任意类型元素(如 int)
- 近似类型元素(使用表示法,如int)
- 联合类型元素(使用|表示法,如int|int64)
需要注意的是,如果interface类型中使用了这三种元素的任意一种,那么这个interface只能用于泛型的类型参数
3.2.2.1 任意类型元素
任意类型(包含interface类型)都可以出现在一个新的interface类型中,用于表示一个仅用于类型参数的集合
type Mint interface {int
}func addMint[M Mint](m1, m2 M) M {return m1 + m2
}
func TestMint(t *testing.T) {t.Log(addMint(3, 4))
}
此时该interface表示的数据集仅包含一种类型,且仅能用于泛型场景中的类型参数中。
interface类型和自定义类型都可以出现在interface中从而表示一个类型集合。
可以定义新的泛型interface,不能使用泛型interface定义interface 方法集合
interface泛型用于interface
但是不能将interface泛型用于interface方法集合
但是如果显式的声明了泛型,那么就可以使用
并且该interface的方法集合也能像之前一样实现
因为在定义interface泛型的时候,限定是int,所以只有int类型才算是实现了方法
这样来看,使用interface泛型类型,可以限定什么样的方法算是实现。
如果将float64加入到interface泛型中,那么float64的方法也算是实现
3.2.2.2 近似类型元素
在使用 interface声明类型集合时,可以使用~<type>
来制定一组类型,只要其底层类型为同一类型即包含在这个集合中。
因为在Go中,可以通过type取别名,而泛型又时通用这个含义。
比如创建一个string的泛型函数,但是因为使用了type对string取了别名,结果别名类型就无法使用泛型函数。
近似类型元素就是可以让type取了别名的类型也能使用
不使用近似类型
使用近似类型
只要底层类型相同,就能使用泛型函数
需要注意的是,
~
之后的类型必须是某个底层类型,换句话说,一个类型的底层类型不是自身就不能使用~
。
另外,interface 自身也不能用于定义近似类型集合,因为interface的底层类型并不确定。
3.2.2.3 联合类型元素
前面使用~
定义的元素集合仅能包括一组底层类型一致的类型,又是可能需要联合多种类型,甚至联合多种底层类型一致的类型,此时可以用
|
定义一个更宽泛的类型集合
type MInteger interface {int | int8 | int16 | int32 | int64
}
但是上述定义仅能支持底层类型,不支持别名
更进一步,可以把所有底层类型也包含进来
type MAnyInteger interface {~int | ~int8 | ~int16 | ~int32 | ~int64
}
这样即使是别名类型,也能支持。
3.2.3 interface类型集合运算
前面使用interface声明类型集合时,均使用一行代码制定一个集合(一个子集),事实上interface支持按行制定多个自己和,这些自己和取交集形成最终的集合
type NewString interface {~stringstring
}
NewString的类型集合由两个子集组成,一个是所有底层类型为string的集合,另一个是string单一类型,两个子集取交集,最终的类型集合将只包含string单一类型
3.2.4 基于操作的类型集合
假设顶一个泛型函数来比较元素大小
type Ordered interface {~int|~int8|~int16|~int32|~int64|~uint|~uint8|~uint16|~uint32|~uint64|~float32|~float64|~string
}func Equals[T Ordered](a, b T) bool {if a == b {return true}return false
}
定义的Ordered泛型类型是全部可以使用==
比较的底层类型,并且包含别名类型,泛型函数限定了Ordered泛型类型。
copmarable和Ordered类似,范围更大。
4. 小例子
4.1 map.Keys 获取map的全部key
将map中的所有key取出来,然后存入切片中返回
func Keys[K comparable, V any](m map[K]V) []K {res := make([]K, 0, len(m))for k, _ := range m {res = append(res, k)}return res
}
类型参数K被用于声明了一个泛型的切片,然后把遍历到的key添加到切片中并返回。
任意的map都能使用Keys泛型函数
func TestKeys(t *testing.T) {t.Log(Keys(map[string]struct{}{"one": {},"tow": {},"three": {},}))t.Log(Keys(map[int]int{1: 1,2: 2,3: 3,}))
}
4.2 Set
Set可以存储一组不重复的数据,广泛用于需要去重的场景。很多编程语言比如Java,C++都提供了相应的实现,但是在Go语言中并没有Set类型。
有了泛型可以自己实现了
// 定义 Set
type Set[T comparable] map[T]struct{}// 创建 Set
func MakeSet[T comparable]() Set[T] {return make(Set[T])
}// 添加元素
func (s Set[T]) Add(k T) {s[k] = struct{}{}
}// 删除元素
func (s Set[T]) Delete(k T) {delete(s, k)
}// 判断是否包含
func (s Set[T]) Contains(k T) bool {_, ok := s[k]return ok
}// 返回长度
func (s Set[T]) Len() int {return len(s)
}// 遍历
func (s Set[T]) Iterate(f func(T)) {for k := range s {f(k)}
}func TestSet(t *testing.T) {set := MakeSet[int]()set.Add(1)set.Add(2)set.Add(1)set.Add(3)t.Log(set.Len()) // 预期 3t.Log(set.Contains(2)) // 预期 trueset.Delete(1) t.Log(set.Len()) // 预期 2t.Log(set.Contains(1)) // 预期 falsesum := 0set.Iterate(func(i int) {sum += i})t.Log(sum) // 预期 5
}
使用泛型实现的Set可适用于任意的可比较类型,行为与其他语言实现的Set基本类似,但是只能函数调用,不能使用下标操作访问元素。
上面实现的Set底层使用一个map实现,并不是线程安全的,还可以进一步使用自定义扩展
type SyncSet[C comparable] struct {l sync.RWMutexm map[C]struct{}
}
在读操作的时候,加读锁,在写操作的时候加写锁。
4.3 排序 Sort
要对切片中的元素进行排序,在标准库提供sort.Slice之前,每种类型的切片都需要实现sort.Interface接口中的三个方法
然后使用sort.Sort方法进行排序,即便后来标准库中引入了sort.Slice,但是使用时仍然需要提供一个排序函数。
使用泛型实现一个针对切片的通用排序函数
type Ordered interface {~int | ~int8 | ~int16 | ~int32 | ~int64 |~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |~float32 | ~float64 |~string
}type orderSlice[O Ordered] []Ofunc (o orderSlice[O]) Len() int {return len(o)
}
func (o orderSlice[O]) Less(i, j int) bool {return o[i] < o[j]
}
func (o orderSlice[O]) Swap(i, j int) {o[i], o[j] = o[j], o[i]
}
func OrderSlice[O Ordered](s []O) {// s 被转换为 []Ordered 类型,也就是 orderSlice,然后排序// 因为 orderSlice已经实现了排序的接口,不需要额外实现了sort.Sort(orderSlice[O](s))
}
func TestOrder(t *testing.T) {is := []int{3, 4, 5, 1, 2}OrderSlice(is)t.Log(is)ss := []string{"he", "ww", "ss"}OrderSlice(ss)t.Log(ss)
}
使用泛型实现排序幻术也有一定的局限性,因为不容易处理复杂的符合类型,比如自定义的struct类型。
5. 总结
反形式衡量编程语言技术完备度的一个重要参考指标,但是也是一个比较争议的技术。
泛型的缺失导致开发者不得不编写重复的代码,或者编写相对通用但缺少类型安全的代码,甚至有些项目使用代码自动生成技术来摆脱编写
重复代码的烦恼,从这方面来看,Go确实需要泛型。
但是引入泛型也是有一定成本的,比如泛型的三个困局:
- 没有泛型(C语言)会降低程序员的生产力,但不会增加语言的复杂度
- 泛型会增加编译器的负担(C++),可能会编译出很多冗余的代码,进而拖慢编译时间
- 泛型会降低运行时的性能(Java),避免编译大量冗余代码的后果是增加运行时的开销
Go语言早在1.17版本时就推出了试用版本,但在1.18中还是用了极大的篇幅说明泛型的种种风险。
https://golang.google.cn/doc/go1.18#generics
相关文章:

Go-知识泛型
Go-知识泛型 1. 认识泛型1.1 不使用泛型1.2 使用泛型 2. 泛型的特点2.1 函数泛化2.2 类型泛化 3. 类型约束3.1 类型集合3.2 interface 类型集合3.2.1 内置interface类型集合3.2.2 自定义interface类型集合3.2.2.1 任意类型元素3.2.2.2 近似类型元素3.2.2.3 联合类型元素 3.2.3 …...
Qt 如何 发送与解析不定长报文以及数组不定长报文
文章目录 割方式一,采用QDataStream 解析,可直接设定大小端解析,无需自己转换方式二,采用结构体字节对齐方式解析发送接收方割 方式一,采用QDataStream 解析,可直接设定大小端解析,无需自己转换 需要注意的是结构体定义要去掉字节对齐,否则会崩溃,因为由自定义数据结…...

Rust默认使用UTF-8编码来解析源代码文件。如果在代码中包含无法用UTF-8编码表示的字符,编译器会报错!
文章目录 Rust默认编码示例在ANSI编码下中文显示正常的代码在UTF-8编码下将显示不正常在编译时,Rust使用UTF-8编码来解析代码,发现无法用UTF-8编码表示的字符,于是编译器报错 Rust默认编码 Rust 语言默认使用 UTF-8 编码来解析源代码文件。如…...

【jeston】torch相关环境安装
参考:玩转NVIDIA Jetson (25)— jetson 安装pytorch和torchvision 我的jeston信息: torch install 安装环境 conda create -n your_env python3.8 conda activate your_envpytorch_for_jeston 安装.whl文件 验证࿱…...

[CR]厚云填补_大型卫星影像去云数据集
AllClear: A Comprehensive Dataset and Benchmark for Cloud Removal in Satellite Imagery Abstract 卫星图像中的云对下游应用构成了重大挑战。当前云移除研究的一个主要挑战是缺乏一个全面的基准和一个足够大和多样化的训练数据集。为了解决这个问题,我们引入了…...

Langchain CharacterTextSplitter无法分割文档问题
在使用Langchain的文档分割器时,使用CharacterTextSplitter拆分文档是,发现返回的文档根本没有变化,即使设置了chunk_size,返回的大小也不符合参数设置。 CharacterTextSplitter设置了150,但是根本没有处理࿰…...
ros service不走是为什么
在ROS(Robot Operating System)中,如果ROS服务(Service)没有正常工作,可能有多种原因。你可以检查以下几点来排查问题: 服务是否正确启动 首先,确保服务节点已经启动并注册了相应的…...
量子计算机的原理与物理实现
量子计算机的原理与物理实现很复杂 指导性原则 首先思考制备一台量子计算机需要些什么? 需要量子比特——二能级量子系统。除了量子计算机需要满足一些物理特性,它还必须要把量子比特绘制到某种初态上,以及测量系统的输出态。 而实验上的挑战…...
SQL Server 常用关键词语法汇总
一、函数 1.1 CAST CAST ( expression AS data_type [ ( length ) ] )expression: 这是你想要转换的数据或表达式。data_type: 目标数据类型,比如 INT, VARCHAR, DATE 等等。(length): 对于某些数据类型(如 CHAR, VARCHAR, BINARY, VARBINARYÿ…...

软件测试工程师面试整理 —— 操作系统与网络基础!
在软件测试中,了解操作系统和网络基础知识对于有效地进行测试工作至关重要。无论是在配置测试环境、调试网络问题,还是在进行性能测试和安全测试时,这些知识都是不可或缺的。 1. 操作系统基础 操作系统(Operating System, OS&am…...
网络安全防御策略:通过限制IP访问提升服务器安全性
标题:网络安全防御策略:通过限制IP访问提升服务器安全性 摘要: 在网络安全领域,服务器被入侵是一场严重的事故。一旦发生这种情况,除了立即采取措施恢复系统外,还需要加强后续的安全防护措施。本文将探讨为…...
Multiprocessing出错没有提示was skipped without notice in python
这个问题可以通过打印返回结果解决。 解决方法 比如 Pool.apply_async(csdnKuangXiaoHU, args=(p, DestFile))改成 Result = Pool.apply_async(csdnKuangXiaoHU, args=...

调整应用窗口透明度
朋友问我有没有软件透明得,一开始没理解,他给我发一个,我一看原来时调整窗口透明度得,想着python应该也可以实现,就写了一个。 效果图如下: 源码如下: import sys import ctypes from PySid…...
启智畅想集装箱号码智能识别原理,OCR识别应用
集装箱号码用途: 集装箱号码在填写托运单时是必填项,用于标识和跟踪货物运输过程中的集装箱。它有助于海关管理和物流跟踪,确保货物能够顺利通过海关检查并按时送达目的地。 集装箱号码智能识别原理: 在深入探讨集装箱号码OCR&…...
React基础知识
说明:react版本为 18.3.1 React是什么 React由Meta公司研发,是一个用于构建Web和原生交互界面的库。(开发基于浏览器的web应用和基于mac和android的移动应用)React的优势 1.相较于传统基于DOM开发的优势:组件化的开…...

Java基础:面向对象编程3
1 Java可变长参数 1.1 概述 Java 的可变长参数(Varargs)是在 Java 1.5 中引入的功能,允许方法接受任意数量的相同类型的参数。可变参数的语法是在参数类型后面加上三个点(...),例如 int... numbers。 1.…...
实验kubernetes的CPU绑定策略
CPU 管理配置 CPU 管理策略通过 kubelet 参数 --cpu-manager-policy 或 KubeletConfiguration 中的 cpuManagerPolicy 字段来指定。 支持两种策略: none:默认策略。static:允许为节点上具有某些资源特征的 Pod 赋予增强的 CPU 亲和性和独占…...

Zsh 安装与配置
目录 1 环境配置 1.1 基本工具安装 1.2 安装 oh-my-zsh 1.3 从.bashrc中迁移配置(可选) 2 主题配置 2.1 内置主题 2.2 自定义主题 2.2.1 推荐主题 3 插件安装 3.1 推荐插件 3.1.1 zsh -autosuggestions 3.1.2 zsh-syntax-highlighting 3.2 启…...

Redis可视化工具Redis Desktop Manager(附安装包)
前言 redis工具,我相信每个开发都需要,如果每次查都去client执行指令,我怕查完之后,老大就要发版咯。我之前一直用的Redis可视化工具RedisDesktopManager,总觉得差点意思,直到同事推荐了个新的,…...
sql server删除过期备份文件脚本
一、通过脚本查看过期文件,时间可以自己设定 for /f "delims" %i in (dir /b /a-d "E:\mybak_file\*.bak" ^| findstr /i "backup" ^| findstr /v /i "no_backup") do if "%~ti" LSS "2024/09/29 16:50&qu…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...