【Golang那些事】go1.22和1.23 更新重点及测评

好久没有写文章了,攒了一年的Golang版本特性的技术点以及踩过的坑,那就在新年第一篇的文章中做一个总结吧:

一、关于迭代器

(一)迭代器去掉了共享共享内存

一个经典的面试题

说到Golang经典的面试题,大家可能都刷到过很多,笔者这里也曾经收藏过几个比较有意思的面试题,下面这个就是其中之一:

package mainimport "fmt"func main() {slice := []int{0, 1, 2, 3}mymap := make(map[int]*int)for index, value := range slice {mymap[index] = &value}for key, value := range mymap {fmt.Printf("map[%v]: %v\n", key, *value)}
}

上面的代码输出结果是什么?

看起来似乎比较简单:

其实,在20230915时间之前,输出的结果为:

map[3]=3
map[0]=3
map[1]=3
map[2]=3

因为for range创建了迭代对象每个元素的副本,而不是直接返回每个元素的引用,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,返回的变量是同一个迭代过程中根据切片依次赋值的变量,所以最终map中存储的地址都是同一个变量的地址,而其值即为最后一次迭代中赋的值

是不是比较容易出错

这里也确实让广大Golang开发者吐槽的地方,所以在golang1.22中,golang官方终于对这里出手了

请在此添加图片描述

可以看到这里官方自己也定义为最常见的Go错误之一

官方吐槽自己也是可以!

参考资料:

https://antonz.org/go-1-22/

https://tip.golang.org/doc/go1.22

(二)迭代器的"For" loops may 支持整数

一个更方便写测试用例的小功能

for i := range 10 {fmt.Print(10 - i, " ")
}
fmt.Println()
fmt.Println("go1.22 has lift-off!")

这个在1.22中进行了添加

参考资料:

https://antonz.org/go-1-22/

https://blog.csdn.net/Wksycxy/article/details/136770738

https://tip.golang.org/ref/spec#For_range

(三)迭代器与range的结合

一致的迭代器形式

历史背景:

Russ发现Go标准库中有很多库(如上截图)中都有迭代器的实现,但形式不统一,没有标准的“实现路径”,各自为战。这与Go面向工程的目标有悖,现状阻碍了大型Go代码库中的代码迁移。因此,Go团队希望给大家带来一致的迭代器形式,具体来说就是允许for range支持对一定类型函数值(function value)进行迭代,即range over func。

2024年2月,iterator以试验特性被Go 1.22版本引入,通过GOEXPERIMENT=rangefunc可以开启range-over-func特性以及使用iter包。
在golang.org/x/exp下面,Go团队还提议维护一个xiter包,这个包内提供了用于组合iterator的基本适配器(adapter),不过目前该xiter包依旧处于proposal状态,尚未落地。
2024年8月,iterator将伴随Go 1.23版本正式落地,现在我们可以通过Go playground在线体验iterator,当然你也可以安装Go tip版本或Go 1.23的rc版在本地体验。

终于在1.23落地了

请在此添加图片描述

这意味着很多用迭代器的地方可以通过range for的形式显示调用

var m sync.Mapm.Store("alice", 11)
m.Store("bob", 12)
m.Store("cindy", 13)// 1.22
m.Range(func(key, value any) bool {fmt.Println(key, value)return true
})// 1.23
for key, val := range m.Range {fmt.Println(key, val)
}

可以看到后面这种方式更加的简洁

还有一些比较简洁的操作可以参考:

https://antonz.org/go-1-23/#timer-changes

二、关于切片

(一)增加了连接函数,顺道在切片修改的函数做了健壮性处理

用好slices是golang开发的一个重要点

关于新的连接函数

s1 := []int{1, 2}
s2 := []int{3, 4}
s3 := []int{5, 6}
res := slices.Concat(s1, s2, s3)
fmt.Println(res)

关于delete函数

请在此添加图片描述

关于Compact和Replace函数

请在此添加图片描述

关于insert函数,更加的强Schema

如果参数 i 超出范围,Insert函数会报panic。

请在此添加图片描述

参考资料:

https://antonz.org/go-1-22/

(二)关于slices.Repeat

用法比较简单:

s := []int{1, 2}
r := slices.Repeat(s, 3)
fmt.Println(r)

输出为:

1 2 1 2 1 2

三、关于随机数

(一)新的随机库

1.22版本提供了一个新的库math/rand/v2

math/rand/v2 并不是 math/rand的升级

这里可以说一说随机数的历史渊源

其实大家对math/rand不是那么满意。
2017年,#20661 中提到math/rand.Read和crypto/rand.Read相近,导致本来应该使用crypto/rand.Read的地方使用了math/rand.Read,导致了安全问题
2017年,#21835 中 Rob Pike 提议在Go 2中使用PCG Source。
2018年,#26263 中 Josh Bleecher Snyder 提议对math/rand进行彻底的重构。
2023年6月, Russ Cox基于先前的对math/rand的吐槽,以及和Rob Pike的讨论,建立了一个讨论(#60751),准备新建一个包math/rand/v2,重新设计和实现一个新的伪随机数的库讨论也很热烈,最后实现了一个提案#61716,这个提案最直接的动机是清理 math/rand 并解决其中许多悬而未决的问题,特别是使用过时生成器、缓慢的算法,以及与 crypto/rand.Read 的不幸冲突。
由于go module的支持版本v2、v3、…, Go 1.22中将会有一个新的包math/rand/v2,这个包将会是一个新的包,而不是math/rand的升级版本。这个包的目标是提供一个更好的伪随机数生成器,它的 API 也更加简单易用,同时一些检查工具也能支持这个包,不会报错。
看样子,math/rand/v2将会是第一个在标准库中建立v2版本的包,如果大家能够接受,将来会有更多的包加入进来,比如sync/v2、encoding/json/v2等等。

信息来源:https://colobu.com/2023/12/24/new-math-rand-in-Go/

可以看到当一个库的问题足够多的时候,总会有优秀的工程师站出来,进行一波大的重构,解决历史问题,这个也适用于官方代码

这里一个简单的结论,关于随技术

1.考虑到安全避免被人预测的场景下,要使用crypto/rand 包。

2.其他的情况一般来说使用ath/rand/v2就够了

参考资料:

https://antonz.org/go-1-22/

https://colobu.com/2023/12/24/new-math-rand-in-Go/

四、关于HTTP库

(一)增加不同方法的Handle调用及URL模糊匹配能力

如果使用Gin等框架的话,其实这个能力已经有了,现在Go1.22版本也提供

官方表示,我也可以主动提供一些框架的能力,如果大家有想写Web框架的话会更友好

###1.下面这里如果填写了POST,则使用专门handler来处理

mux.HandleFunc("POST /items/create", func(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "POST item created")
})mux.HandleFunc("/items/create", func(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "item created")
}){// uses POST routeresp, _ := http.Post(server.URL+"/items/create", "text/plain", nil)body, _ := io.ReadAll(resp.Body)fmt.Println("POST /items/create:", string(body))resp.Body.Close()
}{// uses generic routeresp, _ := http.Get(server.URL+"/items/create")body, _ := io.ReadAll(resp.Body)fmt.Println("GET /items/create:", string(body))resp.Body.Close()
}

输出的结果为:

POST /items/create: POST item created
GET /items/create: item created

###2.下面这里可以使用像:/items/{id}通配符的形式获取参数

mux.HandleFunc("/items/{id}", func(w http.ResponseWriter, r *http.Request) {id := r.PathValue("id")fmt.Fprintf(w, "Item ID = %s", id)
})req, _ := http.NewRequest("GET", server.URL+"/items/12345", nil)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
fmt.Println("GET /items/12345:", string(body))
resp.Body.Close()

以 …结尾的通配符(如 /files/{path…})必须出现在模式的末尾,也可以获取

mux.HandleFunc("/files/{path...}", func(w http.ResponseWriter, r *http.Request) {path := r.PathValue("path")fmt.Fprintf(w, "File path = %s", path)
})req, _ := http.NewRequest("GET", server.URL+"/files/a/b/c", nil)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
fmt.Println("GET /files/a/b/c:", string(body))
resp.Body.Close()

以 / 结尾的模式会一如既往地匹配所有将其作为前缀的路径。要匹配包括尾部斜杠的确切模式,请以 {KaTeX parse error: Expected 'EOF', got '}' at position 1: }̲ 结尾,如 /exact/ma…}:

mux.HandleFunc("/exact/match/{$}", func(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "exact match")
})mux.HandleFunc("/exact/match/", func(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "prefix match")
}){// exact matchreq, _ := http.NewRequest("GET", server.URL+"/exact/match/", nil)resp, _ := http.DefaultClient.Do(req)body, _ := io.ReadAll(resp.Body)fmt.Println("GET /exact/match/:", string(body))resp.Body.Close()
}{// prefix matchreq, _ := http.NewRequest("GET", server.URL+"/exact/match/123", nil)resp, _ := http.DefaultClient.Do(req)body, _ := io.ReadAll(resp.Body)fmt.Println("GET /exact/match/123:", string(body))resp.Body.Close()
}

这里有个细节:

**如果两个模式在匹配的请求中重叠,则更具体的模式优先。**如果两者都不更具体,则模式会发生冲突。此规则概括了原始优先规则,并维护了模式的注册顺序无关紧要的属性。

###3.NewRequestWithContext

新的 NewRequestWithContext 方法创建具有上下文的传入请求。

(二)增加Cookies的方法

如果使用Gin等框架的话,其实这个能力已经有了,现在Go1.23版本也提供

ParseCookie 函数

解析 Cookie 标头值并返回在其中设置的所有 Cookie。由于相同的 Cookie 名称可以多次出现,因此返回的 Value 可以包含给定键的多个值。

line := "session_id=abc123; dnt=1; lang=en; lang=de"
cookies, err := http.ParseCookie(line)
if err != nil {panic(err)
}
for _, cookie := range cookies {fmt.Printf("%s: %s\n", cookie.Name, cookie.Value)
}

ParseSetCookie 函数

解析 Set-Cookie 标头值并返回 Cookie

line := "session_id=abc123; SameSite=None; Secure; Partitioned; Path=/; Domain=.example.com"
cookie, err := http.ParseSetCookie(line)
if err != nil {panic(err)
}
fmt.Println("Name:", cookie.Name)
fmt.Println("Value:", cookie.Value)
fmt.Println("Path:", cookie.Path)
fmt.Println("Domain:", cookie.Domain)
fmt.Println("Secure:", cookie.Secure)
fmt.Println("Partitioned:", cookie.Partitioned)

Request.CookiesNamed函数

检索与给定名称匹配的所有 Cookie

func handler(w http.ResponseWriter, r *http.Request) {cookies := r.CookiesNamed("session")if len(cookies) > 0 {fmt.Fprintf(w, "session cookie = %s", cookies[0].Value)} else {fmt.Fprint(w, "session cookie not found")}
}func main() {req := httptest.NewRequest("GET", "/", nil)req.AddCookie(&http.Cookie{Name: "session", Value: "abc123"})w := httptest.NewRecorder()handler(w, req)resp := w.Result()body, _ := io.ReadAll(resp.Body)fmt.Println(string(body))
}

五、关于计时器

(一)新的资源优化

1.23版本对计时器的资源使用做了优化

关于以下代码

// go 1.22
type token struct{}func consumer(ctx context.Context, in <-chan token) {for {select {case <-in:// do stuffcase <-time.After(time.Hour):// log warningcase <-ctx.Done():return}}
}

会事实上导致广义上的内存泄漏,即虽然有指针指向,但其实应用不在使用了

我们可以做个实验:

编写 100K 通道发送后的内存使用情况:

// go 1.22
func main() {ctx, cancel := context.WithCancel(context.Background())defer cancel()tokens := make(chan token)go consumer(ctx, tokens)memBefore := getAlloc()for range 100000 {tokens <- token{}}memAfter := getAlloc()memUsed := memAfter - memBeforefmt.Printf("Memory used: %d KB\n", memUsed/1024)
}

输出结果为:

Memory used: 24325 KB

这是因为,time.After() 一旦调用了,该计时器在过期之前不会释放,所以这里的内存都积压了

而在 Go 1.23 中,不再被程序引用的 Timer和 Ticker立即符合垃圾回收的条件,即使它们的 Stop 方法尚未被调用。所以不会存在内存积压问题

(二)关于reset的坑

看下面这段代码:

// go 1.22
func main() {const timeout = 10 * time.Millisecondt := time.NewTimer(timeout)time.Sleep(20 * time.Millisecond)start := time.Now()t.Reset(timeout)<-t.Cfmt.Printf("Time elapsed: %dms\n", time.Since(start).Milliseconds())// expected: Time elapsed: 10ms// actual:   Time elapsed:  0ms
}

这里输出为:

Time elapsed: 0ms

因为计时器超时时间设置为 10 毫秒。所以在我们等待 20ms 之后,它已经过期并向 t.C 通道发送了一个值。由于Reset 不重置channel,因此 <-t.C 不会阻塞并立即进行。所以第9行会直接执行,从而打印的时间就是当时的时间(此外,由于 Reset函数重启了计时器,在 10ms 后t.C还会收到一个过期的信号)

Go 1.23 中修复说明:

The timer channel associated with a Timer or Ticker is now unbuffered, with capacity 0. The main effect of this change is that Go now guarantees that for any call to a Reset or Stop method, no stale values prepared before that call will be sent or received after the call.

也就是说通过无缓冲的方式解决了通道重制的问题

详细可以参考:

https://antonz.org/go-1-23/#timer-changes

六、关于“字符串驻留”

(一)unique 包

字符串去重

Go 1.23 标准库引入了一个名为 unique 的新包,旨在实现可比较值的规范化。简而言之,该包允许你对值进行去重,使其指向单个规范的唯一副本,并在底层有效管理这些规范副本

这里写一个比较简单的字符串驻留的函数

var internPool map[string]string// Intern 返回一个与 s 相等的字符串,但该字符串可能与之前传递给 Intern 的字符串共享存储空间。
func Intern(s string) string {pooled, ok := internPool[s]if !ok {// 克隆字符串,以防它是某个更大的字符串的一部分。// 如果正确使用字符串驻留,这种情况应该很少见。pooled = strings.Clone(s)internPool[pooled] = pooled}return pooled
}

当你构建许多可能是重复的字符串时(例如在解析文本格式时),这非常有用。

此实现非常简单,在某些情况下效果很好,但它也存在一些问题:

它永远不会从池中删除字符串。

多个 goroutine 无法安全地同时使用它。

它仅适用于字符串,即使该想法非常通用。

此实现还有一个错失的机会,而且很微妙。在底层,字符串是由指针和长度组成的不可变结构。比较两个字符串时,如果指针不相等,则必须比较它们的内容以确定是否相等。但如果我们知道两个字符串是规范化的,那么只需检查它们的指针就_足够_了。

新的 unique 包引入了一个名为 Make 的函数,类似于 Intern

它的工作方式与 Intern 大致相同。在内部,它也使用全局映射(一个快速的泛型并发映射),Make 在该映射中查找提供的值。但它也与 Intern 有两个重要区别。首先,它接受任何可比较类型的值。其次,它返回一个包装值,即 HandleT,可以从中检索规范值。

HandleT 是设计的关键。HandleT 具有以下属性:当且仅当用于创建它们的_值_相等时,两个 HandleT _值_才相等。更重要的是,比较两个 HandleT 值的成本很低:它归结为指针比较。与比较两个长字符串相比,这要便宜一个数量级!

到目前为止,这在普通的 Go 代码中都能做到。

关于使用

使用这里笔者找到了两个例子,不过笔者觉得后续可以再找一些

// Addr 表示 IPv4 或 IPv6 地址(带或不带范围寻址区域),类似于 net.IP 或 net.IPAddr。
type Addr struct {// 其他不相关的未导出字段...// 有关地址的详细信息,汇总在一起并进行规范化。z unique.Handle[addrDetail]
}// addrDetail 指示地址是 IPv4 还是 IPv6,如果是 IPv6,则指定地址的区域名称。
type addrDetail struct {isV6   bool   // IPv4 为 false,IPv6 为 true。zoneV6 string // 如果 IsV6 为 true,则可能 != ""。
}var z6noz = unique.Make(addrDetail{isV6: true})// WithZone 返回一个与 ip 相同但具有提供的区域的 IP。如果区域为空,则删除该区域。如果 ip 是 IPv4 地址,则 WithZone 是无操作的,并返回未更改的 ip。
func (ip Addr) WithZone(zone string) Addr {if !ip.Is6() {return ip}if zone == "" {ip.z = z6nozreturn ip}ip.z = unique.Make(addrDetail{isV6: true, zoneV6: zone})return ip
}

由于许多 IP 地址可能使用相同的区域,并且此区域是其身份的一部分,因此对它们进行规范化非常有意义。区域的去重减少了每个 netip.Addr 的平均内存占用量,而它们被规范化的事实意味着 netip.Addr 值的比较效率更高,因为比较区域名称变成了简单的指针比较。
具体可以参考:https://k8scat.com/posts/go/go-1.23-unique/

另一个例子:

假设我们有一个随机词生成器:

我们用它从 100 个单词的词汇表中生成 10,000 个单词:

// wordGen returns a generator of random words of length wordLen
// from a set of nDistinct unique words.
func wordGen(nDistinct, wordLen int) func() string {vocab := make([]string, nDistinct)for i := range nDistinct {word := randomString(wordLen)vocab[i] = word}return func() string {word := vocab[rand.Intn(nDistinct)]return strings.Clone(word)}
}// randomString returns a random string of length n.
func randomString(n int) string {// omitted for brevity
}var words []stringfunc main() {const nWords = 10000const nDistinct = 100const wordLen = 40generate := wordGen(nDistinct, wordLen)memBefore := getAlloc()// store wordswords = make([]string, nWords)for i := range nWords {words[i] = generate()}memAfter := getAlloc()memUsed := memAfter - memBeforefmt.Printf("Memory used: %d KB\n", memUsed/1024)
}

运行结果为:

Memory used: 622 KB

而如果用unique

var words []unique.Handle[string]func main() {const nWords = 10000const nDistinct = 100const wordLen = 40generate := wordGen(nDistinct, wordLen)memBefore := getAlloc()// store word handleswords = make([]unique.Handle[string], nWords)for i := range nWords {words[i] = unique.Make(generate())}memAfter := getAlloc()memUsed := memAfter - memBeforefmt.Printf("Memory used: %d KB\n", memUsed/1024)
}

结果为:

Memory used: 96 KB

具体可以参考:https://antonz.org/go-1-23/#timer-changes

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36668.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【css酷炫效果】纯CSS实现照片堆叠效果

【css酷炫效果】纯CSS实现照片堆叠效果 缘创作背景html结构css样式完整代码基础版进阶版(增加鼠标悬停查看) 效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;https://download.csdn.net/download/u011561335/90492022 缘 创作随缘&#xff0c;不定时更新。 创…

labview与西门子1500plc进行S7通讯(仿真效果)

环境&#xff1a; 1.博图V16 2.S7-PLCSIM Advanced V3.0 3.labview2020 4.HslCommunication的dll文件 运行效果图 通过使用HslCommunication的库文件来对西门子plc进行通讯 labview代码 代码打包 通过网盘分享的文件&#xff1a;labview进行s7通讯测试.rar 链接: https:/…

[蓝桥杯 2023 省 B] 飞机降落(不会dfs的看过来)

[蓝桥杯 2023 省 B] 飞机降落 题目描述 N N N 架飞机准备降落到某个只有一条跑道的机场。其中第 i i i 架飞机在 T i T_{i} Ti​ 时刻到达机场上空&#xff0c;到达时它的剩余油料还可以继续盘旋 D i D_{i} Di​ 个单位时间&#xff0c;即它最早可以于 T i T_{i} Ti​ 时刻…

实验1:Vue基础实验

Web前端开发技术实验报告 实验1&#xff1a;Vue基础实验 一、实验目的&#xff1a; 掌握Vue实例的创建方法理解并初步掌握Vue实例的生命周期及钩子函数的使用掌握计算属性与侦听器使用方法 二、实验要求&#xff1a; 掌握Vue的基本语法及使用。编写程序并调试&#xff0c;完…

Spring Cloud 服务监控 - Sleuth + Zipkin 全链路追踪实战

一、为何需要全链路追踪&#xff1f; 在微服务架构中&#xff0c;用户请求通常涉及多个服务的交互&#xff08;如订单→支付→库存&#xff09;。这使得性能瓶颈和故障排查变得更加复杂。传统的日志分析面临两大核心挑战&#xff1a; • 性能瓶颈模糊&#xff1a;当响应延迟增…

数据类设计_图片类设计之6_矩阵图形类设计(前端架构)

前言 学的东西多了,要想办法用出来.C和C是偏向底层的语言,直接与数据打交道.尝试做一些和数据方面相关的内容 引入 接续上一篇,讨论矩阵图形类设计 方法论-现在能做什么 这段属于聊天内容---有句话是这么说的&#xff1a;不要只埋头拉车&#xff0c;还要抬头看路。写代码也是…

OpenCV图像拼接(1)概述

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 此图说明了在Stitcher类中实现的拼接模块流程。使用该类&#xff0c;可以配置/移除某些步骤&#xff0c;即根据特定需求调整拼接流程。流程中的所…

【开原宝藏】30天学会CSS - DAY1 第一课

下面提供一个由浅入深、按步骤拆解的示例教程&#xff0c;让你能从零开始&#xff0c;逐步理解并实现带有旋转及悬停动画的社交图标效果。为了更简单明了&#xff0c;以下示例仅创建四个图标&#xff08;Facebook、Twitter、Google、LinkedIn&#xff09;&#xff0c;并在每一步…

【pytest框架源码分析五】pytest插件的注册流程

前文介绍到pytest整体是运用插件来实现其运行流程的。这里仔细介绍下具体过程。 首先进入main方法 def main(args: list[str] | os.PathLike[str] | None None,plugins: Sequence[str | _PluggyPlugin] | None None, ) -> int | ExitCode:"""Perform an i…

谷歌or-tools开源库入门

1.命令行编译程序 这里要说明下&#xff0c;直接用qt或者VS2022打开cmake工程&#xff0c;编译没有成功。所以&#xff0c;老老实实的按照官方教程来&#xff0c;使用命令行编译。 &#xff08;1&#xff09;准备 1&#xff09;安装cmake&#xff0c;版本3.18以上&#xff0…

Python实现WYY音乐下载

一、需求背景 WYY音乐作为国内主流音乐平台,其歌曲资源丰富但下载接口存在多重加密保护。本文将通过Python结合JS逆向技术,解析其核心加密逻辑,实现免费歌曲的下载功能。 二、技术难点分析 1. 接口加密机制 通过抓包分析可知,网易云核心接口使用两次加密: 第一次:获取…

拥抱健康生活,开启养生之旅

在快节奏的现代生活中&#xff0c;健康养生愈发重要&#xff0c;它不仅能让我们保持良好状态&#xff0c;更是享受美好生活的基石。​ 饮食养生是健康的关键。我们应秉持均衡原则&#xff0c;一日三餐合理搭配。多摄入新鲜蔬果&#xff0c;它们富含维生素、矿物质与膳食纤维&a…

《Waf 火绒终端防护绕过实战:系统程序副本+Certutil木马下载技术详解》

目录 绕过火绒终端安全软件的详细方法 方法一&#xff1a;利用系统程序副本绕过命令监控 方法二&#xff1a;结合certutil.exe副本下载并执行上线木马 注意事项 总结 实际案例解决方案 前提条件 详细操作步骤 1. 攻击主机&#xff08;VPS&#xff09;上的准备工作 2.…

机器学习概要

文章目录 一、什么是机器学习 二、机器学习的种类 1. 有监督学习 2. 无监督学习 3.强化学习 三、机器学习的应用 四、机器学习的步骤 1. 数据的重要性 2. 数据和学习的种类 3. 可视化 一、什么是机器学习 机器学习指的是计算机根据给定的问题、课题或环境进行学习&a…

C# Winform 实现换肤,并自定义皮肤功能

具体实现原理详见 SkinHelp.cs类&#xff0c;实现了对原有控件的重绘&#xff0c;详见源码 public abstract class SkinHelp{private static SkinColor _currentSkinColor SkinColor.Default;private static BackgroundStripe _currentStripe BackgroundStripe.Default;priva…

基于FPGA的3U机箱模拟量高速采样板ADI板卡,应用于轨道交通/电力储能等

板卡简介&#xff1a; 本板为模拟量高速采样板&#xff08;ADI&#xff09;&#xff0c;主要用于电机转速和相电流检测&#xff0c;以实现电机闭环控制。 性能规格&#xff1a; 电源&#xff1a;DC5V&#xff0c;DC3.3V&#xff0c;DC15V&#xff0c;DC24V FPGA&#xff1a;…

python爬虫概述

0x00 python爬虫概述 以豆瓣的选电影模块为例&#xff0c;当查看源代码搜索猫猫的奇幻漂流瓶是搜不到的 这时服务器的工作方式应该是这样的 客户端浏览器第一次访问其实服务器端是返回的一个框架(html代码) 当客户端浏览器第二次通过脚本等方式进行访问时服务器端才返回的数据…

win10 如何用我的笔记本 接网线 远程控制 台式机

1.查看笔记本ip&#xff0c;台式机ip。确保在同一网段 可以ping通 1.1 ip在同一网段&#xff0c;但是ping不通 1.解决&#xff1a;把双方防火墙关闭 2.解决&#xff1a;当前网口&#xff0c;先禁用再启用 以上两台电脑就可以ping通了 2.设置双方电脑 启动远程控制 此电脑-》…

给管理商场消防安全搭建消防安全培训小程序全过程

一、需求沟通 “我是管理商场消防安全的嘛&#xff0c;做这个的作用呢&#xff0c;1是商场的所有商户员工可以看平面或者视频随时自学&#xff0c; 2是我们定期培训必修课程、考试&#xff0c;这个需要留存他们的手签字的签到表确认我们讲给他们听了&#xff08;免责很重要&am…

可视化图解算法:链表中倒数(最后)k个结点

1. 题目 描述 输入一个长度为 n 的链表&#xff0c;设链表中的元素的值为ai &#xff0c;返回该链表中倒数第k个节点。 如果该链表长度小于k&#xff0c;请返回一个长度为 0 的链表。 数据范围&#xff1a;0≤n≤105&#xff0c;0 ≤ai≤109&#xff0c;0 ≤k≤109 要求&am…