【go语言】AST抽象语法树详解&实践之扫描代码生成错误码文档

2024-01-21

背景

为了能识别出代码中抛出错误码的地址和具体的错误码值,再根据错误码文件获取到错误码的具体值和注释,方便后续的排错,这里使用AST进行语法分析获取到代码中的目标对象。

一、编译过程

在开始解析代码之前先补充了解一下编译过程

编译过程是将高级语言源代码转换为目标机器代码或其他中间表示的过程。它通常包括以下几个主要阶段:

词法分析(Lexical Analysis):

  • 输入:源代码
  • 输出:词法单元(tokens)
  • 任务:将源代码分解为基本的词法单元,如关键字、标识符、运算符等

语法分析(Syntax Analysis):

  • 输入:词法单元序列
  • 输出:语法树(Abstract Syntax Tree,AST)或其他中间表示
  • 任务:根据语法规则检查词法单元序列的结构,构建语法树表示源代码的语法结构。

语义分析(Semantic Analysis):

  • 输入:语法树或中间表示
  • 输出:语义信息
  • 任务:检查源代码的语义正确性,捕捉并处理语法上合法但语义上错误的结构,生成符号表等。

中间代码生成(Intermediate Code Generation):

  • 输入:语法树或中间表示
  • 输出:中间代码
  • 任务:将语法树转换为一种中间表示,以便进行后续优化和目标代码生成。

优化(Optimization):

  • 输入:中间代码
  • 输出:优化后的中间代码
  • 任务:对中间代码进行各种优化,提高程序性能、减小代码体积等。

目标代码生成(Code Generation):

  • 输入:优化后的中间代码
  • 输出:目标机器代码或汇编代码
  • 任务:将中间代码转换为目标机器代码,根据目标平台生成可执行文件。

链接(Linking):

  • 输入:目标机器代码或汇编代码,可能包括库文件
  • 输出:可执行文件
  • 任务:将多个目标文件链接在一起,解决符号引用、地址重定位等问题,生成最终的可执行文件。

二、AST

AST 指的是 Abstract Syntax Tree(抽象语法树),它是编程语言中源代码语法结构的一种抽象表示形式。AST 通常是一个树形结构,用于表示程序的语法层次关系,从而方便进行语法分析和处理。

在编程语言中,源代码是由一系列的词法单元(token)组成的,而 AST 则将这些词法单元按照语法规则组织成一个树形结构。每个节点代表源代码中的一个语法结构,例如表达式、语句、函数声明等。

先看一下demo代码和对应的生成的部分AST树:

package main

import (
	"errors"
	"log"
)

var DivisionByZero = errors.New("除0错误")
var LessThanZero = errors.New("小于0错误")

func divide(a, b int) (int, error) {
	if b == 0 {
		log.Default().Panic(DivisionByZero)
		return 0, DivisionByZero
	}
	if a < 0 {
		log.Default().Panic(LessThanZero)
		return 0, LessThanZero
	}
	return a / b, nil
}
     0  *ast.File {
     1  .  Package: example.go:1:1
     2  .  Name: *ast.Ident {
     3  .  .  NamePos: example.go:1:9
     4  .  .  Name: "main"
     5  .  }
     6  .  Decls: []ast.Decl (len = 4) {
     7  .  .  0: *ast.GenDecl {
     8  .  .  .  TokPos: example.go:3:1
     9  .  .  .  Tok: import
    10  .  .  .  Lparen: example.go:3:8
    11  .  .  .  Specs: []ast.Spec (len = 2) {
    12  .  .  .  .  0: *ast.ImportSpec {
    13  .  .  .  .  .  Path: *ast.BasicLit {
    14  .  .  .  .  .  .  ValuePos: example.go:4:2
    15  .  .  .  .  .  .  Kind: STRING
    16  .  .  .  .  .  .  Value: "\"errors\""
    17  .  .  .  .  .  }
    18  .  .  .  .  .  EndPos: -
    19  .  .  .  .  }
    20  .  .  .  .  1: *ast.ImportSpec {
    21  .  .  .  .  .  Path: *ast.BasicLit {
    22  .  .  .  .  .  .  ValuePos: example.go:5:2
    23  .  .  .  .  .  .  Kind: STRING
    24  .  .  .  .  .  .  Value: "\"log\""
    25  .  .  .  .  .  }
    26  .  .  .  .  .  EndPos: -
    27  .  .  .  .  }
    28  .  .  .  }
    29  .  .  .  Rparen: example.go:6:1
    30  .  .  }
    31  .  .  1: *ast.GenDecl {
    32  .  .  .  TokPos: example.go:8:1
    33  .  .  .  Tok: var
    34  .  .  .  Lparen: -
    35  .  .  .  Specs: []ast.Spec (len = 1) {
    36  .  .  .  .  0: *ast.ValueSpec {
    37  .  .  .  .  .  Names: []*ast.Ident (len = 1) {
    38  .  .  .  .  .  .  0: *ast.Ident {
    39  .  .  .  .  .  .  .  NamePos: example.go:8:5
    40  .  .  .  .  .  .  .  Name: "DivisionByZero"
    41  .  .  .  .  .  .  .  Obj: *ast.Object {
    42  .  .  .  .  .  .  .  .  Kind: var
    43  .  .  .  .  .  .  .  .  Name: "DivisionByZero"
    44  .  .  .  .  .  .  .  .  Decl: *(obj @ 36)
    45  .  .  .  .  .  .  .  .  Data: 0
    46  .  .  .  .  .  .  .  }
    47  .  .  .  .  .  .  }
    48  .  .  .  .  .  }
    49  .  .  .  .  .  Values: []ast.Expr (len = 1) {
    50  .  .  .  .  .  .  0: *ast.CallExpr {
    51  .  .  .  .  .  .  .  Fun: *ast.SelectorExpr {
    52  .  .  .  .  .  .  .  .  X: *ast.Ident {
    53  .  .  .  .  .  .  .  .  .  NamePos: example.go:8:22
    54  .  .  .  .  .  .  .  .  .  Name: "errors"
    55  .  .  .  .  .  .  .  .  }
    56  .  .  .  .  .  .  .  .  Sel: *ast.Ident {
    57  .  .  .  .  .  .  .  .  .  NamePos: example.go:8:29
    58  .  .  .  .  .  .  .  .  .  Name: "New"
    59  .  .  .  .  .  .  .  .  }
    60  .  .  .  .  .  .  .  }
    61  .  .  .  .  .  .  .  Lparen: example.go:8:32
    62  .  .  .  .  .  .  .  Args: []ast.Expr (len = 1) {
    63  .  .  .  .  .  .  .  .  0: *ast.BasicLit {
    64  .  .  .  .  .  .  .  .  .  ValuePos: example.go:8:33
    65  .  .  .  .  .  .  .  .  .  Kind: STRING
    66  .  .  .  .  .  .  .  .  .  Value: "\"除0错误\""
    67  .  .  .  .  .  .  .  .  }
    68  .  .  .  .  .  .  .  }
    69  .  .  .  .  .  .  .  Ellipsis: -
    70  .  .  .  .  .  .  .  Rparen: example.go:8:45
    71  .  .  .  .  .  .  }
    72  .  .  .  .  .  }
    73  .  .  .  .  }
    74  .  .  .  }
    75  .  .  .  Rparen: -
    76  .  .  }

1. Token

在编程语言中,Token(词法单元)是源代码中的最小语法单元,是编译器或解释器在进行词法分析时识别和处理的基本单位。Token 是源代码经过词法分析器处理后得到的标识符,它代表了源代码中的不可分割的词法结构。

不同类型的编程语言有不同的 Token 类型,一般包括以下几类:

  1. 关键字(Keywords): 表示编程语言的保留字,具有特殊含义。例如,在 C 语言中, if else while 等就是关键字。

  2. 标识符(Identifiers): 表示变量、函数、类等的名称。标识符需要遵循一定的命名规则。例如, variableName 是一个标识符。

  3. 常量(Literals): 表示固定值的词法单元,包括整数、浮点数、字符串等。例如, 42 3.14 "Hello, World!" 都是常量。

  4. 运算符(Operators): 表示执行操作的符号,如加法、减法、乘法等。例如, + - * 是运算符。

  5. 分隔符(Delimiters): 表示源代码结构的分隔符,如括号、分号、逗号等。例如, ( ) ; 是分隔符。

  6. 注释(Comments): 表示注释内容,编译器或解释器通常会忽略它们。例如, // This is a comment

Token 的产生是由词法分析器(Lexer 或 Scanner)负责的,它扫描源代码,将字符序列组合成有意义的 Token。Token 提供给语法分析器(Parser)使用,用于构建 AST(抽象语法树)等进一步的语法分析和语义分析。

下面是一个简单的示例,展示了一个小型程序的源代码和对应的一些 Token:

# 源代码 x = 10 + 5

对应的 Token:

Token(IDENT, "x") 
Token(ASSIGN, "=") 
Token(INT, "10") 
Token(PLUS, "+") 
Token(INT, "5")

在这个例子中, Token(IDENT, "x") 表示一个标识符 Token, Token(ASSIGN, "=") 表示一个赋值操作符 Token, Token(INT, "10") 表示一个整数常量 Token,以此类推。

Go 语言中的 Token类型:

2. FileSet

先看代码:使用 token.NewFileSet() 创建一个新的 FileSet 对象,并使用 parser.ParseFile 函数解析 Go 代码文件。

// 解析 Go 代码文件
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)

token 包中的 FileSet 类型是用于跟踪源代码文件和位置信息的结构。 FileSet 用于维护源代码文件的集合,每个文件都关联有唯一的标识符,并记录了源代码中各个位置的行号、列号等信息。

让我们看一下 parser.ParseFile ()函数源码:

func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast.File, err error) {
	if fset == nil {
		panic("parser.ParseFile: no token.FileSet provided (fset == nil)")
	}

	// get source
	text, err := readSource(filename, src)
	if err != nil {
		return nil, err
	}

	var p parser
	defer func() {
		if e := recover(); e != nil {
			// resume same panic if it's not a bailout
			bail, ok := e.(bailout)
			if !ok {
				panic(e)
			} else if bail.msg != "" {
				p.errors.Add(p.file.Position(bail.pos), bail.msg)
			}
		}

		// set result values
		if f == nil {
			// source is not a valid Go source file - satisfy
			// ParseFile API and return a valid (but) empty
			// *ast.File
			f = &ast.File{
				Name:  new(ast.Ident),
				Scope: ast.NewScope(nil),
			}
		}

		p.errors.Sort()
		err = p.errors.Err()
	}()

	// parse source
	p.init(fset, filename, text, mode)
	f = p.parseFile()

	return
}

参数:

fset *token.FileSet : 用于记录位置信息的文件集合。

filename string : 源文件的文件名。

src any : 源代码,可以是字符串、字节数组或 io.Reader

mode Mode : 控制解析器的模式,例如是否跳过对象解析阶段。

返回值:

f *ast.File : 解析后的 AST 树,表示整个源文件的语法结构。

err error : 解析过程中的错误,如果有的话。

内部实现:

parser 结构体是用于实际解析的解析器对象。

readSource 函数用于获取源代码文本,它可以从文件或其他来源获取。

p.parseFile() 调用实际执行解析过程,返回解析得到的 AST。

错误处理:

如果源代码无法读取,返回的 AST 是空的,但不为 nil。

如果源代码读取成功但存在语法错误,返回的 AST 包含 ast.Bad* 节点来表示错误的源代码片段。

多个错误通过 scanner.ErrorList 返回,按源代码位置排序。

panic 恢复:

解析器在遇到错误时可能会触发 panic,但会通过 recover 恢复,确保程序不会崩溃。

如果 panic 是 bailout 类型且有错误消息,将错误添加到解析器的错误列表中。

让我们再看一下其中的初始化函数p.init():解析器对象的初始化过程,设置了解析器的一些基本属性,初始化了文件对象和词法分析器,并准备开始解析源代码。

func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
	p.file = fset.AddFile(filename, -1, len(src))
	eh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }
	p.scanner.Init(p.file, src, eh, scanner.ScanComments)

	p.top = true
	p.mode = mode
	p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
	p.next()
}

继续看一下核心的解析函数:解释看注释

// parseFile 解析整个 Go 源文件,返回相应的 ast.File 节点。
func (p *parser) parseFile() *ast.File {
	if p.trace {
		defer un(trace(p, "File"))
	}

	// 如果在扫描第一个标记时存在错误,则不必继续解析。
	// 可能根本不是有效的 Go 源文件。
	if p.errors.Len() != 0 {
		return nil
	}

	// 包子句
	doc := p.leadComment
	pos := p.expect(token.PACKAGE)
	// Go 规范:包子句不是声明;
	// 包名不会出现在任何范围中。
	ident := p.parseIdent()
	if ident.Name == "_" && p.mode&DeclarationErrors != 0 {
		p.error(p.pos, "无效的包名 _")
	}
	p.expectSemi()

	// 如果在解析包子句时存在错误,则不必继续解析。
	// 可能根本不是有效的 Go 源文件。
	if p.errors.Len() != 0 {
		return nil
	}

	var decls []ast.Decl
	if p.mode&PackageClauseOnly == 0 {
		// 导入声明
		for p.tok == token.IMPORT {
			decls = append(decls, p.parseGenDecl(token.IMPORT, p.parseImportSpec))
		}

		if p.mode&ImportsOnly == 0 {
			// 包体的其余部分
			prev := token.IMPORT
			for p.tok != token.EOF {
				// 继续接受导入声明以容忍错误,但是报告警告。
				if p.tok == token.IMPORT && prev != token.IMPORT {
					p.error(p.pos, "导入必须出现在其他声明之前")
				}
				prev = p.tok

				decls = append(decls, p.parseDecl(declStart))
			}
		}
	}

	// 构建 ast.File 对象
	f := &ast.File{
		Doc:       doc,
		Package:   pos,
		Name:      ident,
		Decls:     decls,
		FileStart: token.Pos(p.file.Base()),
		FileEnd:   token.Pos(p.file.Base() + p.file.Size()),
		Imports:   p.imports,
		Comments:  p.comments,
		GoVersion: p.goVersion,
	}
	var declErr func(token.Pos, string)
	if p.mode&DeclarationErrors != 0 {
		declErr = p.error
	}
	if p.mode&SkipObjectResolution == 0 {
		resolveFile(f, p.file, declErr)
	}

	return f
}

核心的解析函数resolveFile:调用ast.Walk函数获得代码的AST树

func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, string)) {
	pkgScope := ast.NewScope(nil)
	r := &resolver{
		handle:   handle,
		declErr:  declErr,
		topScope: pkgScope,
		pkgScope: pkgScope,
		depth:    1,
	}

	for _, decl := range file.Decls {
		ast.Walk(r, decl)
	}

	r.closeScope()
	assert(r.topScope == nil, "unbalanced scopes")
	assert(r.labelScope == nil, "unbalanced label scopes")

	// resolve global identifiers within the same file
	i := 0
	for _, ident := range r.unresolved {
		// i <= index for current ident
		assert(ident.Obj == unresolved, "object already resolved")
		ident.Obj = r.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
		if ident.Obj == nil {
			r.unresolved[i] = ident
			i++
		} else if debugResolve {
			pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos()
			r.trace("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
		}
	}
	file.Scope = r.pkgScope
	file.Unresolved = r.unresolved[0:i]
}

最后得到*ast.File ,可以对其进行后续特定的分析处理

3. AST类型

在 Go 语言中,AST(抽象语法树)主要由三个大类别的节点构成,它们分别是:

基本节点(Basic Nodes):

  • ast.Ident 代表标识符(Identifiers),如变量名、函数名等。
  • ast.BasicLit 代表基本的字面量,如整数、浮点数、字符串等。
  • ast.CompositeLit 代表复合字面量,如数组、切片、字典等。

语句节点(Statement Nodes):

  • ast.ExprStmt 代表一个表达式语句,即只包含一个表达式的语句。
  • ast.AssignStmt 代表赋值语句,包括简单的赋值和多重赋值。
  • ast.IfStmt 代表条件语句。
  • ast.ForStmt 代表循环语句。
  • ast.SwitchStmt 代表开关语句( switch )。
  • ast.RangeStmt 代表 for range 循环语句。

声明节点(Declaration Nodes):

  • ast.GenDecl 代表通用声明,用于表示 import const var 等声明。
  • ast.FuncDecl 代表函数声明。
  • ast.TypeSpec 代表类型声明。

所有节点都实现了ast.Node接口,返回了Node的位置信息

type Node interface {
	Pos() token.Pos // position of first character belonging to the node
	End() token.Pos // position of first character immediately after the node
}

// All expression nodes implement the Expr interface.
type Expr interface {
	Node
	exprNode()
}

// All statement nodes implement the Stmt interface.
type Stmt interface {
	Node
	stmtNode()
}

// All declaration nodes implement the Decl interface.
type Decl interface {
	Node
	declNode()
}

以下是ast包中包含的节点类型,可以用到的时候查看官方文档: ast package - go/ast - Go Packages

4. 示例

现在我要获取代码中错误码出现的位置以及对应的名称,下面是源代码

package main

import (
	"errors"
	"log"
)

var DivisionByZero = errors.New("除0错误")
var LessThanZero = errors.New("小于0错误")

func divide(a, b int) (int, error) {
	if b == 0 {
		log.Panic(DivisionByZero)
		return 0, DivisionByZero
	}
	if a < 0 {
		log.Panic(LessThanZero)
		return 0, LessThanZero
	}
	return a / b, nil
}

下面是解析函数: 根据源代码生成的AST节点,遍历一遍获取到需要的声明

package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
)

func main() {
	// 文件路径
	path := "example.go"
	// 创建 FileSet
	fset := token.NewFileSet()
	// 解析 Go 代码文件
	f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
	if err != nil {
		fmt.Printf("Error parsing file: %v\n", err)
		os.Exit(1)
	}
	// 在这里可以使用 fset 和 f 进行进一步的操作,例如访问 AST 等
	// Walk through the AST to find error codes
	ast.Inspect(f, func(n ast.Node) bool {
        //表示函数调用表达式。
		if callExpr, ok := n.(*ast.CallExpr); ok {
			if fun, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
				if ident, ok := fun.X.(*ast.Ident); ok {
					if ident.Name == "log" && fun.Sel.Name == "Panic" {
						if len(callExpr.Args) > 0 {
							if arg, ok := callExpr.Args[0].(*ast.Ident); ok {
								fmt.Printf("错误码名称:%s\n", arg.Name)
								fmt.Printf("出现的位置:%s\n", fset.Position(arg.Pos()))
							}
						}
					}
				}
			}
		}
		return true
	})

}

不是很清楚的多debug几遍就大概知道了:

结果如下:

参考资料

https://github.com/chai2010/go-ast-book

ast package - go/ast - Go Packages

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【go语言】AST抽象语法树详解&实践之扫描代码生成错误码文档 的相关文章

  • 如何让供应商与 Google App Engine 配合使用?

    我正在尝试引入 Go 供应商 将依赖项存储在名为的文件夹中 vendor 到现有的 App Engine 项目 我已将所有依赖项存储在供应商文件夹中 使用 Godep 作为助手 它看起来是正确的 但在本地运行应用程序时出现以下错误 go a
  • 切片文字中的求值顺序

    我最近浏览了Go的 语言规范 https golang org ref spec Order of evaluation https golang org ref spec Order of evaluation但发现评估顺序与本文档中解释
  • struct{} 和 struct{}{} 在 Go 中如何工作?

    我想知道 Go 中的 struct 和 struct 是什么意思 示例如下 array index struct or make map type struct struct is a keyword https golang org re
  • 如何用 Go 将多个字符串解析为模板?

    有没有像这样的简单方法template ParseFiles base html home html 但是对于字符串来说 如何从一组字符串构建模板 我有一个基本模板和一个页面模板列表 全部为字符串 我想在基本模板之上构建它们 我想出了如何合
  • ConstantTimeByteEq 如何工作?

    在大神的密码库里 找到了这个函数ConstantTimeByteEq http golang org src pkg crypto subtle constant time go s 897 936 L17 它有什么作用 如何工作 Cons
  • 从头开始使用映像部署无法启动

    我正在使用以下内容构建图像Dockerfile FROM golang 1 19 2 bullseye as builder COPY src src WORKDIR src RUN CGO ENABLED 1 go build race
  • 使用私有 git 存储库时的 go 工作区结构

    当你不使用 github 时 我一直在尝试找出 go 代码 工作区的标准文件夹布局 结构 我可以看到如何go get在获取 github 项目时有效 但假设我自己的 git 项目不会有 github com username project
  • Go中funcappend的实现在哪里?

    我对 go 非常感兴趣 并尝试阅读 go 函数的实现 我发现其中一些函数没有实现 如追加或调用 The append built in function appends elements to the end of a slice If i
  • 为什么结构体不能转换为嵌入类型

    package main type Inner struct x int type Outer struct Inner func main x Inner 1 y Outer x cannot convert x type Inner t
  • 当所有通道都关闭时中断 select 语句

    我有两个独立生成数据的 goroutine 每个将其发送到一个通道 在我的主 goroutine 中 我想在每个输出进入时使用它们 但不关心它们进入的顺序 每个通道在耗尽其输出时都会自行关闭 虽然 select 语句是像这样独立使用输入的最
  • 在 Alpine 中找不到运行时/cgo

    In an alpine edge我安装的容器通过 RUN apk add no cache musl dev go 我试着跑go get github com golang protobuf protoc gen go then 这会导致
  • Google Cloud Kubernetes 上任务队列的替代方案

    我发现任务队列主要用于App Engine标准环境 我正在将现有服务从 App Engine 迁移到 Kubernetes 任务队列的一个好的替代方案是什么 推送队列是当前正在使用的队列 我在线阅读文档并浏览了此链接 何时使用 PubSub
  • ReverseProxy取决于golang中的request.Body

    我想构建一个 http 反向代理 它检查 HTTP 主体 然后将 HTTP 请求发送到它的上游服务器 你怎么能在 Go 中做到这一点 初始尝试 如下 失败 因为 ReverseProxy 复制传入请求 修改它并发送 但正文已被读取 func
  • Golang 优雅地关闭 HTTP 服务器并进行错误处理

    我正在让我的 HTTP 服务器正常关闭 我从帖子中获取了提示here https stackoverflow com questions 39320025 how to stop http listenandserve 并且到目前为止已经像
  • 初始化嵌套匿名结构

    我有一个 json 作为 fields time id status customerId additionalDetail pageInfo start 0 rows 1000 我想将我的结构编组到上面的 json 并创建如下结构 typ
  • 在 Go 中跟踪 HTTP 请求时指定超时

    我知道通过执行以下操作来指定 HTTP 请求超时的常用方法 httpClient http Client Timeout time Duration 5 time Second 但是 我似乎不知道在跟踪 HTTP 请求时如何执行相同的操作
  • 为什么结构中“[0]byte”的位置很重要?

    0 byte在golang中不应该占用任何内存空间 但这两个结构体的大小不同 type bar2 struct A int 0 byte type bar3 struct 0 byte A int 那么为什么这个位置 0 byte这里重要吗
  • Go io.Pipe 的缓冲版本

    有缓冲版本吗io Pipe https golang org pkg io Pipe 在标准库或第三方库中 在我推出自己的库之前 上下文 我正在尝试使用这个解决方案 https stackoverflow com a 36229262 15
  • 如何分析 VSCode 中函数的性能

    我用 C Golang 编写了一个程序 如何找到占用最高 CPU 周期的函数 目的是提高正在执行的程序的性能 2021 年 10 月 金香儿哈娜 https github com hyangah宣布 tweet https twitter
  • 如何解析 Content-Disposition 标头以检索文件名属性?

    使用 go 如何解析从 http HEAD 请求检索到的 Content Disposition 标头以获取文件的文件名 此外 如何从 http HEAD 响应中检索标头本身 这样的事情正确吗 resp err http Head http

随机推荐

  • Redis基础系列-哨兵模式

    Redis基础系列 哨兵模式 文章目录 Redis基础系列 哨兵模式 1 引言 2 什么是哨兵模式 3 哨兵模式的配置 4 哨兵模式的启动和验证 4 1 主master宕机 看会出现什么问题
  • 获取员工其当前的薪水比其manager当前薪水还高的相关信息

    后悔早签三方了 双非渣渣不该信金九银十 凯子今年确实也卷起来了 想进华为 就别去华为实习 鼠鼠拿了几个offer 请大家帮忙看下 有点困惑百度 base北京 n 4 16 薪资可 华为开奖 一个月过去了 25终于找到JAVA实习 算法方向推
  • 【go语言】结构体数据填充生成md错误码文件

    这里使用pongo2这个模版引擎库进行md文件渲染 GitHub flosch pongo2 Django syntax like template engine for Go package main import fmt github
  • 【go语言】读取toml文件

    一 简介 TOML 全称为Tom s Obvious Minimal Language 是一种易读的配置文件格式 旨在成为一个极简的数据序列化语言 TOML的设计原则之一是保持简洁性 易读性 同时提供足够的灵活性以满足各种应用场景 TOML
  • 都在做端到端了,传统规控/轨迹预测还能坚持几年?

    最近有些做传统规控和轨迹预测的小伙伴问我 现在LLM做端到端自动驾驶这么火 自己要不要考虑转行 坚持传统规控 轨迹预测还能做几年 会不会三五年后失业了 而且最近小米汽车发布会 也说已经搭载了端到端感知决策大模型 全球首次用于量产车 这个问题
  • 深圳三维扫描分析/偏差检测模具型腔三维尺寸及形位偏差测量公司

    CASAIM中科广电三维扫描模具型腔深圳案例 模具型腔的三维扫描分析 偏差检测是一项重要的质量控制过程 旨在确保模具制造过程中的精确度和一致性 CASAIM中科广电通过使用高精度的三维扫描设备 可以获取模具型腔的实际形状和尺寸数据 并将其与
  • ICLR2024 | ReSimAD:如何在没有真实数据的情况下,提升感知模型的泛化性能

    点击下方 卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 今天自动驾驶之心为大家分享 ICLR 2024刚刚中稿的ReSimAD ReSimAD可以极大地提升感知模型对于目标域场景的泛化能力 甚至比一些无监督领域适配的方法还要
  • 回望计算机视觉会议ICCV的31年

    作者 原野寻踪 编辑 汽车人 原文链接 https zhuanlan zhihu com p 670393313 点击下方 卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心 全栈算法 技术交流群 本文只做
  • pip问题们

    pip问题们 下载指定版本 已经安装的库列表保存到文本文件中 根据依赖文件批量安装库 离线安装库 whl 文件 pip install git https xxxx安装失败解决方法 下载指定版本 安装特定版本的package 通过使用 gt
  • 上汽飞凡,突然换帅了。。。

    作者 有据无车 编辑 智能车参考 点击下方 卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心技术交流群 本文只做学术分享 如有侵权 联系删文 飞凡汽车 突然换帅了 最近 上汽集团官方宣布 乘用车总经理 飞
  • FL Studio2024国内中文版多少钱?有哪些新功能呢

    购买您想要的最新的水果软件FL2024版本 然后所有未来所有该版本更新都是免费的 再也不用付钱了 在音乐软件行业 更新费用通常在 960 1600 元之间 而 FL Studio 将始终免费为您提供最新版本 因为我们相信 只要我们开发 FL
  • 视频剪辑软件Camtasia2024最新版本快捷键大全

    Camtasia Studio是一款专门录制屏幕动作的工具 它能在任何颜色模式下轻松地记录 屏幕动作 包括影像 音效 鼠标移动轨迹 解说声音等等 今天来给大家介绍一下Camtasia快捷键的相关内容 Camtasia也是一个十分好用的电脑屏
  • 【EI复现】基于深度强化学习的微能源网能量管理与优化策略研究(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 2 1 有 无策略奖励 2 2 训练结果1
  • 题解 | #连续两次作答试卷的最大时间窗#

    程序员到大神成长书单 最无语的公司 写代码让领导不开心了咋办 快手秋招急招 暑期实习也可以投 找实习需要实习经历 操作系统岗 简历能走吗 面了南网数字集团 面试官问的问题有两个挺尖锐的 这种是不是说明不太想要我 俗话说打蛇打七寸 我感觉面试
  • 查找薪水记录超15条的员工号emp_no以及其对应的记录次数

    理想 大模型面经 23届试用期没通过 还能找到工作吗 有没有啥厂招往届生啊 腾讯音乐前端暑期实习一面 已oc 华为od 机试 面试面经 华为OD技术岗面经汇总 软开 算法 测试岗 想看一下大家看法 一个月过去了 25终于找到JAVA实习 华
  • 短信系统搭建主要因素|网页短信平台开发源码

    短信系统搭建主要因素 网页短信平台开发源码 随着移动互联网的快速发展 短信系统已成为企业和个人进行信息传递的重要工具 建立一个高效可靠的短信系统对于企业来说非常重要 下面我们将介绍一些影响短信系统搭建的主要因素 1 平台选择 在搭建短信系统
  • 2024最新MathType7.4.10中文版下载安装激活教程

    MathType 是一款专业的数学公式编辑工具 提供交互式编辑器 让你在编辑数学试卷 书籍 报刊 论文 幻灯演示等文档轻松输入各种复杂的数学公式和符号 1 本次讲解mathtype 7版本的安装步骤 MathType2024 Win 安装包
  • 『力扣刷题本』:逆波兰表达式求值

    大家好久不昂 最近 1 个多月罗根一直在备考期末 文章发的很少 现在已经放寒假啦 学习自然也不能拉下 毕竟 4 月份就要去参加蓝桥杯了 先给自己定个小目标 日更 2 篇 咳咳 下面马上开始讲题 一 题目 给你一个字符串数组 tokens 表
  • GitLab CI 实现项目A更新代码自动触发项目B更新错误码文档

    一 CI CD简介 CI CD 是持续集成 Continuous Integration 和持续交付 持续部署 Continuous Delivery Continuous Deployment 的缩写 是一种软件开发和交付的最佳实践 这两
  • 【go语言】AST抽象语法树详解&实践之扫描代码生成错误码文档

    背景 为了能识别出代码中抛出错误码的地址和具体的错误码值 再根据错误码文件获取到错误码的具体值和注释 方便后续的排错 这里使用AST进行语法分析获取到代码中的目标对象 一 编译过程 在开始解析代码之前先补充了解一下编译过程 编译过程是将高级