Go基础编程 - 11 - 函数(func)

news2024/11/15 8:51:38

接口(interface)

    • 函数
      • 1. 函数定义
        • 1.1. 函数名
        • 1.2. 参数列表
        • 1.3. 返回值列表
      • 2. 匿名函数
      • 3. 闭包、递归
        • 3.1 闭包
          • 3.1.1 函数、引用环境
          • 3.1.2 闭包的延迟绑定
          • 3.1.3 goroutine 的延迟绑定
        • 3.2 递归函数
      • 4. 延迟调用(defer)
        • 4.1 defer特性及用途
        • 4.2 defer与闭包
        • 4.3 defer f.Close
        • 4.4. defer陷阱
          • 4.4.1. defer 与 closure
          • 4.4.1. defer nil 函数
      • 5. 高阶函数
      • 6. 异常处理

上一篇:接口(interface)
下一篇:流程控制


函数

Go语言函数的特点:

1. 无需声明原型
2. 支持不定变参
3. 支持多返回值
4. 支持命名返回参数
5. 函数也是一种类型,一个函数可以复制给变量;可以作为参数传递给其他函数
6. 不支持嵌套(一个包不能有重名的函数)
7. 不支持重载
8. 不支持默认参数

1. 函数定义

使用关键字 func 定义函数,左大括号不能另起一行。

func 函数名(参数列表)(返回值列表) { // 左侧大括号不能另起一行
    函数体
}
1.1. 函数名

函数名是函数的标识符,在Go语言中,函数名必须遵循标识符的命名规则。

1.2. 参数列表

参数列表在函数定义时指出。函数定义时的参数,称为函数的形参;当调用函数时,传递过来的变量就是函数的实参

  • 函数可以没有参数,也可以有多个参数,参数之间用逗号隔开。
  • 参数列表中,类型在变量名之后。
  • 类型相同的相邻参数,参数类型可合并。
  • 不定参数传值 就是函数的参数不是固定的,后面的类型是固定的; 可使用interface{}定义任意类型的不定参数。
  • 不定参数必须是参数列表中最后一个参数。
package main

import "fmt"

// 无参数
func sayHello() {
    fmt.Println("Hello, world!")
}

// 相同类型的相邻参数,参数类型可合并
func add(x, y int) int {
    return x + y
}

// 不定参数,为同一类型,用...表示;接收到的参数为切片类型
func sum(nums...int) int {
    // 参数nums为切片类型
    sum := 0
    for _, num := range nums {
        sum += num
    }
    return sum
}

// 使用interface{}定义任意类型的不定参数,使用类型断言接收参数值。
func myFunc(args ...interface{}) {
    name, _ := args[0].(string)
    age, _ := args[1].(int)
    fmt.Printf("%s今年%d岁!", name, age)
}

func main() {
    sayHello()
    fmt.Println(add(1, 2))
    fmt.Println(sum(1, 2, 3, 4, 5))
    fmt.Println(sum([]int{1, 2, 3}...)) // 使用slice做变参传入是,须使用“...”展开slice
    myFunc("小明", 20)
}

值传递和引用传递
无论是值传递,还是引用传递,传递给函数的都是参数的副本。不过,值传递是值的copy,引用传递是地址的copy。
map、slice、chan、指针、interface默认以引用的方式传递。

package main

func modifyArray(arr [3]int) [3]int {
    for i := range arr {
        arr[i] = arr[i] * 10
    }
    return arr
}

// 1.如果是对数组某个完整元素值的进行修改,那么原有实参数组不变;
// 2.如果是对数组某个元素(切片)内的某个元素的值进行修改,那么原有数据也会跟着改变;
// 传参可以理解为浅copy,参数本身的指针是不同的,但元素指针相同,对元素指针所指向的内容操作会影响到传参过程中的原始数据。
func modifyMultiArray(arr [3][]int) [3][]int {
    for i := range arr[0] {
        arr[0][i] = arr[0][i] * 10  // 实际修改的为arr[0]引用类型值的指针所指向的内存的值,原始实参元素指向同样内存,因此也改变原始实参数据。
    }
    arr[1][2] = 60
    arr[2] = []int{7, 8, 9} // 修改整个引用类型元素的值,实际是给arr[2]重新赋值了一个指向新slice的指针值,原始实参元素指向的内存并未改变,因此不影响原始实参数据。
    return arr
}

func main() {
    arr := [3]int{1, 2, 3}
    arrRes := modifyArray(arr)
    fmt.Println(arr)    // [1 2 3],值传递函数内部修改并未改变arr变量
    fmt.Println(arrRes) // [10 20 30]
    
    arrSlice := [3][]int{
        {1, 2, 3},
        {4, 5, 6},
    }
    arrSlice1 := modifyMultiArray(arrSlice)
    fmt.Println(arrSlice)   // [[10 20 30] [4 5 60] []]
    fmt.Println(arrSlice1) // [[10 20 30] [4 5 60] [7 8 9]]
}

在这里插入图片描述

1.3. 返回值列表
  • 函数可以返回任意数量的返回值,返回值之间用逗号隔开,多返回值必须用括号。
  • 可以使用 “_” 标识符忽略函数的某个返回值。
  • Go语言返回值可以被命名,但不可与形参重名,且必须在函数体中使用;命名返回参数可看做与形参类似的局部变量,最后由 return 隐式返回。
  • 有返回值的函数,必须有明确的终止(return)语句,否则会引发编译错误。
  • Go语言不能使用容器接收多返回值,必须使用多个变量,或者“_”忽略。
package main

import "fmt"

func numbers() (int, int){ // 多返回值
    return 6, 8
}

func add(x, y int) int { // 单个返回值,可省略括号
    return x + y
}

func calc(a, b int) (sum int, avg int) {  // 命名返回值必须使用括号,且不可与形参重名 
    sum = a + b
    avg = (a + b) / 2
    return  
}

func main() {
    //多返回值函数,必须使用多个变量接收,或“_”忽略。
    //var s = make([]int, 2)
    //s = calc(2, 4)	//报错:assignment mismatch: 1 variable but calc returns 2 values
    sum, _ := calc(2, 4)
    fmt.Println(sum)    //6

    // 返回值作为其他函数调用实参
    fmt.Println(add(numbers()))
}

命名返回参数可被同名局部变量遮蔽,此时需要显式返回。

func add(x, y int) (sum int) {
    //var sum = 0 //同级代码块内变量不可重声明 Error:sum redeclared in this block
    { //子代码块
        var sum = x + y
        // return   //不可使用隐式返回 Error:result parameter sum not in scope at return
        return sum
    }
    return  // 隐式返回 0
}

命名返回参数允许 defer 延迟调用通过闭包读取和修改。

func calc(a, b int) (sum int, avg int) {
    defer func() {
        sum += 100
    }()

    sum = a + b
    avg = (a + b) / 2
    return
}

func add(x, y int) int {
    var sum int

    defer func() {
        sum += 100  // 修改无效
    }()

    sum = x + y
    return sum
}

func main(){
    sum, avg := calc(6, 10)
    fmt.Println(sum, avg) // 116 8

    fmt.Println(add(2, 8)) // 10
}

2. 匿名函数

匿名函数由一个不带函数名的函数声明和函数体构成。优势是可以直接使用函数内的变量,不必申明。

package main

import (
    "fmt"
    "math"
)

func main() {
    // Golang可以赋值给变量,做为结构字段,或在channel中传送

    // 变量
    getSqrt := func(a float64) float64 {
        return math.Sqrt(a)
    }
    fmt.Println(getSqrt(4))

    // collection 
    cfn := []()func() string{
        func() string { return "hello" },
        func() string { return "world" },
    }

    // as field
    s := struct{
        fn func() string
    }{
        fn: func() string {
            return "hello"
        },
    }   
    fmt.Println(s.fn())

    // channel
    cf := make(chan func() string, 2)
    fc <- func() string { return "Say hello" }
    fmt.Println((<-fc)())
}

3. 闭包、递归

3.1 闭包

参考资料

3.1.1 函数、引用环境

闭包是有函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)

**函数:**在闭包实际实现的时候,往往通过一个外部函数返回其内部函数来实现。内部函数可能是内部实名函数匿名函数或则一个lambda表达式

引用环境:

  • 在函数式语言中,当内嵌函数体内引用到函数体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。引用环境是指在程序执行中的某个点所有处于活跃状态的约束(一个变量的名称和其所代表的对象之间的联系)所组成的集合。
  • 由于闭包把函数和运行时的引用环境打包成一个新的整体,所以就解决了函数编程中的嵌套所引发的问题。当每次调用包含闭包的函数是都将返回一个新的闭包实例,这些实例之间是隔离的,分别包含调用时不同的引用环境现场。

闭包与外部函数的生命周期
内函数对外函数的变量的修改,是对变量的引用。变量被引用后,它所在的函数结束,这个变量也不会马上被销毁;相当于变相延长了函数的生命周期。

package main

import "fmt"

func incrIn(n int) func() int {
    fmt.Printf("%p, %d\n", &n, n)
    return func() int {
        n++ // 内函数对外函数的变量的修改,是对变量的引用
        fmt.Printf("%p, %d\n", &n, n)
        return n
    }
}

func incr1(i *int) func() {
    // 自由变量为引用传递,闭包则不再封闭,修改全局可见的变量,也会对闭包内的这个变量造成影响。
    return func() {
        *i += 1
        fmt.Println(*i)
    }
}

func incr2(i int) func() {
    return func() {
        i += 1
        fmt.Println(i)
    }
}

func main() {
    n := incrIn(100)()
    fmt.Printf("%d\n\n", n)

    i := 100
    f1 := incr1(&i)
    f2 := incr2(i)

    f1() //101   
    f1() //102
    f2() //101
    f2() //102
    fmt.Println()

    i = 1000
    f1() //1001  
    f1() //1002
    f2() //103
    f2() //104
    fmt.Println()

    incr1(&i)() // 1003
    incr1(&i)() // 1004
    incr2(i)() // 1005  每次调用都返回独立的闭包实例,这些实例是隔离的
    incr2(i)() // 1005
    fmt.Println()
}
3.1.2 闭包的延迟绑定
func delay1(x int) []func() {
    var fns []func()
    data := []int{1, 2, 3}

    for _, val := range data {
        fns = append(fns, func() {
            fmt.Printf("%d + %d = %d\n", x, val, x+val)
        })
    }
    return fns
}

func delay2() func() {
    x := 1
    fn := func() {
        fmt.Printf("x = %d\n", x)
    }

    x = 100
    return fn
}

func main(){
    fns := delay1(100)
    for _, fn := range fns {
        fn()
    }
    // 输出:
    // 100 + 3 = 103
    // 100 + 3 = 103
    // 100 + 3 = 103

    delay2()()  // 100
}

上面代码解析:

闭包会保存相关引用的环境,也就是说变量在闭包的生命周期得到了保证;因此在执行闭包的时候,会去外部环境寻找最新的值。

delay1()返回的仅是闭包函数的定义,只有在执行fn()是在真正执行了闭包;执行时寻找最新的值3delay2可以更直观的看到,实际执行的为x最新值100

3.1.3 goroutine 的延迟绑定
func show(v interface{}) {
    fmt.Printf("show v: %v\n", v)
}

func gor1() {
    data := []int{1, 2, 3}
    for _, v := range data {
        go show(v)
    }
}

func gor2() {
    data := []int{1, 2, 3}
    for _, v := range data {
        go func() {
            fmt.Printf("gor2 v: %v\n", v)
        }()
    }
}

var ch = make(chan int, 10)

func gor3() {
    for v := range ch {
        go func() {
            fmt.Printf("gor 3 v: %v\n", v)  // 闭包, v为引用
        }()
    }
}

func main() {
    gor1()  // goroutine执行顺序是随机的
    // 输出:
    // show v: 2
    // show v: 1
    // show v: 3

    gor2()
    // 输出:
    // gor2 v: 3
    // gor2 v: 3
    // gor2 v: 3

    go gor3()
    ch <- 1
    ch <- 2
    ch <- 3
    ch <- 4
    ch <- 11
    time.Sleep(time.Duration(1) * time.Nanosecond)
    ch <- 12
    time.Sleep(time.Duration(1) * time.Nanosecond)
    ch <- 13
    time.Sleep(time.Duration(1) * time.Nanosecond)
    ch <- 15
    // 输出:随机输出,大部分为11,个别为1~4
    // gor 3 v: 11
    // gor 3 v: 3
    // gor 3 v: 12
    // gor 3 v: 11
    // gor 3 v: 11
    // gor 3 v: 11
    // gor 3 v: 13
    // gor 3 v: 15

    time.Sleep(5 * time.Second)
}

上面代码解析:

gor2()内的匿名函数就是闭包(参考闭包内部函数的定义),v为引用,且延长了v的生命周期,在gor2()中for-loop的遍历几乎是“瞬时”完成的,goroutine真正被执行在其后。所以输出都为 3。

gor3()中,加入Sleep机制,使得goroutine在赋值前执行。输出结果与赋值及goroutine执行时v的实际值有关

3.2 递归函数

递归,就是在运行的过程中调用自己。一个函数调用自己,就叫递归函数。

package main

import "fmt"

func factorial(i int) int {
    if i <= 1 {
        return 1
    }
    return i * factorial(i-1)
}

func main() {
    var i int = 7
    fmt.Printf("Factorial of %d is %d\n", i, factorial(i))
}

4. 延迟调用(defer)

4.1 defer特性及用途

defer 特性

  1. 关键字 defer 注册延迟调用。
  2. 延迟调用直到 return 前才被执行。因此可以用来做资源清理。
  3. 多个 defer 语句,按先进后出的方式执行。
  4. defer 语句中的变量,在 defer 声明时就已经决定了。

defer 用途

  1. 关闭文件句柄。
  2. 锁资源释放。
  3. 数据库连接释放。
package main

import (
    "fmt"
    "os"
)

func main() {
    var whatever [3]struct{}

    for i := range whatever {
        defer fmt.Println(i)
    }

    //输出:
    // 2
    // 1
    // 0
}
4.2 defer与闭包

defer中使用的匿名函数依然是一个闭包。

package main

import "fmt"

func main() {

    var whatever [5]struct{}
    for i := range whatever {
        //函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4(i最新值),所以输出全都是4。
        defer func() { fmt.Println(i) }()
    }

    x, y := 1, 2

    defer func(a int) {
        fmt.Printf("x:%d,y:%d\n", a, y) // defer匿名函数为闭包, y为引用。
    }(x) // 复制 x 的值

    x += 100
    y += 100
    fmt.Println(x, y)
    fmt.Println()

    // 输出:
    // 101 202
    // x:1,y:202
    // 4
    // 4
    // 4
    // 4
    // 4
}
4.3 defer f.Close
package main

import "fmt"

type Test struct {
	name string
}

func (T *Test) Close() {
	fmt.Println(T.name, "closed")
}

func Closure() {
    //delay5()
    ts := []Test{{"a"}, {"b"}, {"c"}}

    for _, t := range ts {
        // defer 后面的语句在执行的时候,函数调用的参数会被保存起来,但不执行,也就是复制了一份。

        // 方式1
        defer t.Close()

        // 方式2
        tt := t // tt为内函数变量,不受外部函数变量改变影响。
        defer tt.Close()
    }
    // 方式1 输出:
    // c closed
    // c closed
    // c closed

    // 方式2 输出:
    // c closed
    // b closed
    // a closed
}
4.4. defer陷阱
4.4.1. defer 与 closure
package main

import (
    "errors"
    "fmt"
)

func foo(a, b int) (i int, err error) {
    // 如果 defer 后面跟的不是一个闭包(closure),最后执行的时候我们得到的并不是最新的值。

    defer fmt.Printf("first defer err:%v\n", err)  // 非闭包

    defer func(err error) {
        fmt.Printf("second  defer err:%v\n", err) // 非闭包 
    }(err)

    defer func() {
        fmt.Printf("third  defer err:%v\n", err)   // 闭包
    }()

    if b == 0 {
        err = errors.New("divided by zero")
        return
    }

    i = a / b
    return
}

func main() {

    foo(8, 0)
    // 输出
    // third  defer err:divided by zero
    // second  defer err:<nil>
    // first defer err:<nil>
}
4.4.1. defer nil 函数
package main

import (
    "fmt"
)

func test() {
    var run func() = nil
    defer run()
    fmt.Println("runs")
}

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()
    test()

    // 输出
    // runs
    // runtime error: invalid memory address or nil pointer dereference
}

test函数运行结束,然后defer函数会被执行,且因为值为nil而产生panic异常。要注意的是,run()的声明是没有问题,因为在test函数执行完成后它才会被调用。

5. 高阶函数

高阶函数满足下面两个条件:

  1. 接受其他的函数作为参数传入
  2. 把其他的函数作为结果返回

函数类型
函数类型属于引用类型,它的值可以为nil,零值为nil。

package main

// 函数类型声明
type operate func (x, y int) int

func Calc(x, y int, op operate) (int, error) {
    if op == nil {
        return 0, errors.New("invalid operation")
    }
    return op(x, y), nil
}

func getCalc(op operate) func(x, y int) (int, error) {
    return func(x, y int) (int, error) {
        if op == nil {
            return 0, errors.New("invalid operation")
        }
        return op(x, y), nil
    }
}

func main() {
    var op operate
    fmt.Printf("%T, %#v", op, op) // main.operate, (main.operate)(nil)

    op = func(x, y int) int { return x + y }
    n, _ := Calc(100, 100, op)
    fmt.Println(n) // 200

    add := getCalc(op)
    v, err := add(10, 10)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(v) // 20
    }
}

6. 异常处理

Go语言没有结构化异常,使用panic函数来抛出异常,recover捕获异常。
panic、recover 参数类型为interface{},可以处理任意类型。

  • panicrecover 参数类型为 interface{},可以处理任意类型。
  • 利用 recover 处理 panic 指令,defer 必须放在 panic 之前定义。
  • recover 只有在 defer 调用的函数中才有效,否则当 panic 时,recover 无法捕获到 panic,无法防止 panic 扩散。
  • recover 处理异常后,逻辑并不会回复到 panic 那个点去,函数跑到 defer 之后的那个点。
  • 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。
package main

import (
	"fmt"
)

func demo1() {
    defer func() {
        // 验证是否有panic
        if err := recover(); err != nil {
            fmt.Println("报错:", err)
        }
    }()

    ch := make(chan int, 10)
    close(ch)
    ch <- 1 // 向已关闭的通道发送数据,引发panic
}

func demo2() {
    defer func() {
        fmt.Println("报错:", recover())
    }()

    defer func() {
        // defer 中引发的错误,可以被后续延迟调用捕获,但仅最后一个错误可被捕获。
        panic("defer panic")
    }()

    panic("code panic")
}

func main() {
    demo1() //报错: send on closed channel

    demo2() //报错: defer panic
}

recover函数只有在defer内直接调用才会终止错误,否则返回nil。任何未捕获的错误都会沿用堆栈向外传递


func except() {
    fmt.Println("函数输出错误:", recover())
}

// recover 只有在 defer 调用的函数中才有效。
func main() {

    defer except()  // 有效

    defer func() {  // 有效
        fmt.Println("报错:", recover())
        panic("again panic")
    }()

    defer recover() // 无效 nil

    defer fmt.Println("无效:", recover()) // 无效 nil

    defer func() {
        func() {
            fmt.Println("defer:", recover()) // 无效 nil
        }()
    }()

    panic("panic error!")

    // 输出
    // defer: <nil>
    // 报错: panic error!
    // 函数输出错误: again panic
    // 无效: <nil>
}

常用的异常处理方式

  • 保护代码段,将代码块重构成匿名函数。
func div1(x, y int) int {
    var z int

    func() {
        defer func() {
            if recover() != nil {
                z = 0
            }
        }()

        if y == 0 {
            panic("division by zero")
        }

        z = x / y
    }()

    return z
}
func main() {
    fmt.Println(div1(100, 10))  // 0
    fmt.Println(div1(100, 0))   // 10    
    fmt.Println()
}
  • 除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态.
    标准库 errors.Newfmt.Errorf 函数用于创建实现 error 接口的错误对象,通过判断错误对象实例来确定具体错误类型。

如何区别使用 panicerror导致关键流程出现不可修复性错误使用 panic,其它使用 error

var errDivZero = errors.New("division by zero")

func div(x, y int) (int, error) {
	if y == 0 {
		return 0, errDivZero
	}
	return x / y, nil
}

func main() {
	defer func() {
		fmt.Println("错误:", recover())
	}()

	switch z, err := div(100, 0); err {
	case nil:
		fmt.Println("结果:", z)
	case errDivZero:
		panic(err)
	}
}
  • Go 实现类似 try catch
func Try(fn func(), handler func(interface{})) {

    defer func() {
        if err := recover(); err != nil {
            handler(err)
        }
    }()

    fn()
}

func main() {
    Try(func() {
        panic("Try panic")
    }, func(err interface{}) {
        fmt.Println(err)
    })
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言中的控制语句(三):while 和 do while 语句

文章目录 [toc] &#x1f34a;自我介绍&#x1f34a;while语句&#x1f34a;do...while循环&#x1f34a;&#x1f34a; 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello…

单调栈(随缘复习到了,顺手刷了)

也是不知道为什么突然又复习到单调栈了&#xff0c;所以顺手刷了三道题&#xff0c;总结一下 P6503 [COCI2010-2011#3] DIFERENCIJA 思路&#xff1a;这题是要求每个子区间里面的最大值和最小值的差&#xff0c;我们一开始想的必然是纯暴力呀&#xff0c;但是一看这数据&#…

主流硬派SUV齐聚尼三锅,方程豹豹5成功冲顶

随着汽车的飞速普及&#xff0c;越来越多的车主都喜欢上了越野。 而在浩瀚无垠的沙漠腹地&#xff0c;尼三锅以其独特的地理形态&#xff0c;成为了无数越野爱好者心中的圣地与试炼场。 近日&#xff0c;众多汽车博主携自己的爱车普拉多、方程豹豹5、牧马人、坦克400、坦克700、…

图书馆座位管理系统 /图书馆座位预约系统/图书馆管理系统的设计与实现

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

excel批量新建多个同类型的表格

背景引入 比如&#xff0c;一个企业有多个部门&#xff0c;现在需要按照某一个excel表模板收集各个部门的信息&#xff0c;需要创建数十个同类型表格&#xff0c;且标题要包含部门名称。 1.修改模板表格标题 在一个文件夹下面放入需要发放给各个部门的表格&#xff0c;将标题…

轨迹优化 | 基于ESDF的共轭梯度优化算法(附ROS C++/Python仿真)

目录 0 专栏介绍1 数值优化&#xff1a;共轭梯度法2 基于共轭梯度法的轨迹优化2.1 障碍约束函数2.2 曲率约束函数2.3 平滑约束函数 3 算法仿真3.1 ROS C实现3.2 Python实现 0 专栏介绍 &#x1f525;课程设计、毕业设计、创新竞赛、学术研究必备&#xff01;本专栏涉及更高阶的…

Chapter17 表面着色器——Shader入门精要学习

Chapter17 表面着色器 一、编译指令1.表面函数2.光照函数3.其他可选参数 二、两个结构体1.Input 结构体&#xff1a;数据来源2.SurfaceOutput 三、Unity背后做了什么四、表面着色器的缺点 一、编译指令 作用&#xff1a;指明该表面着色器的 表面函数 和 光照函数&#xff0c;并…

LeetCode 58.最后一个单词的长度 C++

LeetCode 58.最后一个单词的长度 C 思路&#x1f914;&#xff1a; 先解决当最后字符为空格的情况&#xff0c;如果最后字符为空格下标就往后移动&#xff0c;直到不为空格才停止&#xff0c;然后用rfind查询空格找到的就是最后一个单词的起始位置&#xff0c;最后相减就是单词…

前台文本直接取数据库值doFieldSQL插入SQL

实现功能&#xff1a;根据选择的车间主任带出角色。 实现步骤&#xff1a;OA的“字段联动”功能下拉选项带不出表“hrmrolemembers”&#xff0c;所以采用此方法。 doFieldSQL("select roleid from HrmResource as a inner join hrmrolemembers as b on a.id b.resource…

AGI 之 【Hugging Face】 的【从零训练Transformer模型】之二 [ 从零训练一个模型 ] 的简单整理

AGI 之 【Hugging Face】 的【从零训练Transformer模型】之二 [ 从零训练一个模型 ] 的简单整理 目录 AGI 之 【Hugging Face】 的【从零训练Transformer模型】之二 [ 从零训练一个模型 ] 的简单整理 一、简单介绍 二、Transformer 1、模型架构 2、应用场景 3、Hugging …

Flink架构底层原理详解:案例解析(43天)

系列文章目录 一、Flink架构&#xff08;掌握&#xff09; 二、Flink代码案例&#xff08;掌握&#xff09; 三、UDF&#xff08;熟悉&#xff09; 四、Flink常见面试题整理 文章目录 系列文章目录前言一、Flink架构&#xff08;掌握&#xff09;1、系统架构1.1 通信&#xff…

海康威视工业相机SDK+Python+PyQt开发数据采集系统(支持软件触发、编码器触发)

海康威视工业相机SDKPythonPyQt开发数据采集系统&#xff08;支持软件触发、编码器触发&#xff09; pythonpyqt开发海康相机数据采集系统 1 开发软件功能&#xff1a; 支持搜索相机&#xff1a;Gige相机设备和USB相机设备支持两种触发模式&#xff1a;软件触发和编码器触发支…

Docker、containerd、CRI-O 和 runc 之间的区别

容器与 Docker 这个名称并不紧密相关。你可以使用其他工具来运行容器 您可以使用 Docker 或一堆非Docker 的其他工具来运行容器。docker只是众多选项之一&#xff0c;Docker&#xff08;公司&#xff09;在生态系统中创建了一些很棒的工具&#xff0c;但不是全部。 容器方面有…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十三章 驱动模块传参

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

【PPT把当前页输出为图片】及【PPT导出图片模糊】的解决方法(sci论文图片清晰度)

【PPT把当前页输出为图片】及【PPT导出图片模糊】的解决方法 内容一&#xff1a;ppt把当前页输出为图片&#xff1a;内容二&#xff1a;ppt导出图片模糊的解决方法&#xff1a;方法&#xff1a;步骤1&#xff1a;打开注册表编辑器步骤2&#xff1a;修改注册表&#xff1a; 该文…

使用jacob文字生成语音文件时遇到的问题及解决方案

使用jacob文字生成语音文件时 出现如下错误 java.lang.UnsatisfiedLinkError: no jacob-1.18-x64 in java.library.path错误表明Java虚拟机无法在其指定的java.library.path路径中找到名为jacob-1.18-x64的本地库文件。这个错误通常发生在尝试通过JNI或者JNA调用本地库时&…

算法学习笔记(Hello算法)—— 初识算法

1、相关链接 Hello算法&#xff1a;Hello 算法 (hello-algo.com) 2、算法是什么 2.1 算法定义 算法是一系列明确、有限且有效的步骤或指令的集合&#xff0c;用于解决特定问题或执行特定任务。 算法具有以下基本特征&#xff1a; 输入&#xff1a;算法至少有一个输入&…

【python】PyQt5中QAbstractButton基类的特性详细分析与实战应用

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

聚水潭·奇门对接打通金蝶云星空订单查询接口与销售退货新增接口

聚水潭奇门对接打通金蝶云星空订单查询接口与销售退货新增接口 对接源平台:聚水潭奇门 聚水潭SaaSERP于2014年4月上线&#xff0c;目前累计超过2.5万商家注册使用&#xff0c;成为淘宝应用服务市场ERP类目商家数和商家月订单增速最快的ERP。2014年及2015年“双十一”当天&#…

TQSDRPI开发板教程:实现PL端的UDP回环与GPSDO

本教程将完成一个全面的UDP运行流程与GPSDO测试&#xff0c;从下载项目的源代码开始&#xff0c;通过编译过程&#xff0c;最终将项目部署到目标板卡上运行演示。此外&#xff0c;我们还介绍如何修改板卡的IP地址&#xff0c;以便更好地适应您的网络环境或项目需求。 首先从Gi…