跳转到主要内容

标签(标签)

资源精选(342) Go开发(108) Go语言(103) Go(99) angular(82) LLM(75) 大语言模型(63) 人工智能(53) 前端开发(50) LangChain(43) golang(43) 机器学习(39) Go工程师(38) Go程序员(38) Go开发者(36) React(33) Go基础(29) Python(24) Vue(22) Web开发(20) Web技术(19) 精选资源(19) 深度学习(19) Java(18) ChatGTP(17) Cookie(16) android(16) 前端框架(13) JavaScript(13) Next.js(12) 安卓(11) typescript(10) 资料精选(10) NLP(10) 第三方Cookie(9) Redwoodjs(9) LLMOps(9) Go语言中级开发(9) 自然语言处理(9) 聊天机器人(9) PostgreSQL(9) 区块链(9) mlops(9) 安全(9) 全栈开发(8) ChatGPT(8) OpenAI(8) Linux(8) AI(8) GraphQL(8) iOS(8) 软件架构(7) Go语言高级开发(7) AWS(7) C++(7) 数据科学(7) whisper(6) Prisma(6) 隐私保护(6) RAG(6) JSON(6) DevOps(6) 数据可视化(6) wasm(6) 计算机视觉(6) 算法(6) Rust(6) 微服务(6) 隐私沙盒(5) FedCM(5) 语音识别(5) Angular开发(5) 快速应用开发(5) 提示工程(5) Agent(5) LLaMA(5) 低代码开发(5) Go测试(5) gorm(5) REST API(5) 推荐系统(5) WebAssembly(5) GameDev(5) CMS(5) CSS(5) machine-learning(5) 机器人(5) 游戏开发(5) Blockchain(5) Web安全(5) Kotlin(5) 低代码平台(5) 机器学习资源(5) Go资源(5) Nodejs(5) PHP(5) Swift(5) 智能体(4) devin(4) Blitz(4) javascript框架(4) Redwood(4) GDPR(4) 生成式人工智能(4) Angular16(4) Alpaca(4) SAML(4) JWT(4) JSON处理(4) Go并发(4) kafka(4) 移动开发(4) 移动应用(4) security(4) 隐私(4) spring-boot(4) 物联网(4) nextjs(4) 网络安全(4) API(4) Ruby(4) 信息安全(4) flutter(4) 专家智能体(3) Chrome(3) CHIPS(3) 3PC(3) SSE(3) 人工智能软件工程师(3) LLM Agent(3) Remix(3) Ubuntu(3) GPT4All(3) 软件开发(3) 问答系统(3) 开发工具(3) 最佳实践(3) RxJS(3) SSR(3) Node.js(3) Dolly(3) 移动应用开发(3) 编程语言(3) 低代码(3) IAM(3) Web框架(3) CORS(3) 基准测试(3) Go语言数据库开发(3) Oauth2(3) 并发(3) 主题(3) Theme(3) earth(3) nginx(3) 软件工程(3) azure(3) keycloak(3) 生产力工具(3) gpt3(3) 工作流(3) C(3) jupyter(3) 认证(3) prometheus(3) GAN(3) Spring(3) 逆向工程(3) 应用安全(3) Docker(3) Django(3) R(3) .NET(3) 大数据(3) Hacking(3) 渗透测试(3) C++资源(3) Mac(3) 微信小程序(3) Python资源(3) JHipster(3) 大型语言模型(2) 语言模型(2) 可穿戴设备(2) JDK(2) SQL(2) Apache(2) Hashicorp Vault(2) Spring Cloud Vault(2) Go语言Web开发(2) Go测试工程师(2) WebSocket(2) 容器化(2) AES(2) 加密(2) 输入验证(2) ORM(2) Fiber(2) Postgres(2) Gorilla Mux(2) Go数据库开发(2) 模块(2) 泛型(2) 指针(2) HTTP(2) PostgreSQL开发(2) Vault(2) K8s(2) Spring boot(2) R语言(2) 深度学习资源(2) 半监督学习(2) semi-supervised-learning(2) architecture(2) 普罗米修斯(2) 嵌入模型(2) productivity(2) 编码(2) Qt(2) 前端(2) Rust语言(2) NeRF(2) 神经辐射场(2) 元宇宙(2) CPP(2) 数据分析(2) spark(2) 流处理(2) Ionic(2) 人体姿势估计(2) human-pose-estimation(2) 视频处理(2) deep-learning(2) kotlin语言(2) kotlin开发(2) burp(2) Chatbot(2) npm(2) quantum(2) OCR(2) 游戏(2) game(2) 内容管理系统(2) MySQL(2) python-books(2) pentest(2) opengl(2) IDE(2) 漏洞赏金(2) Web(2) 知识图谱(2) PyTorch(2) 数据库(2) reverse-engineering(2) 数据工程(2) swift开发(2) rest(2) robotics(2) ios-animation(2) 知识蒸馏(2) 安卓开发(2) nestjs(2) solidity(2) 爬虫(2) 面试(2) 容器(2) C++精选(2) 人工智能资源(2) Machine Learning(2) 备忘单(2) 编程书籍(2) angular资源(2) 速查表(2) cheatsheets(2) SecOps(2) mlops资源(2) R资源(2) DDD(2) 架构设计模式(2) 量化(2) Hacking资源(2) 强化学习(2) flask(2) 设计(2) 性能(2) Sysadmin(2) 系统管理员(2) Java资源(2) 机器学习精选(2) android资源(2) android-UI(2) Mac资源(2) iOS资源(2) Vue资源(2) flutter资源(2) JavaScript精选(2) JavaScript资源(2) Rust开发(2) deeplearning(2) RAD(2)

介绍


可变参数函数是接受零、一个或多个值作为单个参数的函数。 虽然可变参数函数并不常见,但它们可用于使您的代码更清晰、更具可读性。

可变参数函数比看起来更常见。 最常见的是 fmt 包中的 Println 函数。

func Println(a ...interface{}) (n int, err error)


带有以一组省略号 (...) 开头的参数的函数被视为可变参数函数。 省略号表示提供的参数可以是零、一个或多个值。 对于 fmt.Println 包,它声明参数 a 是可变参数。

让我们创建一个使用 fmt.Println 函数并传入零个、一个或多个值的程序:

print.go

package main

import "fmt"

func main() {
    fmt.Println()
    fmt.Println("one")
    fmt.Println("one", "two")
    fmt.Println("one", "two", "three")
}

第一次调用 fmt.Println 时,我们不传递任何参数。 第二次调用 fmt.Println 时,我们只传入一个参数,值为 1。 然后我们通过一和二,最后是一、二和三。

让我们使用以下命令运行程序:

go run print.go


我们将看到以下输出:

Output

one
one two
one two three

 

输出的第一行是空白的。 这是因为我们在第一次调用 fmt.Println 时没有传递任何参数。 第二次打印一的值。 然后是一二,最后是一、二、三。

现在我们已经了解了如何调用可变参数函数,让我们看看如何定义自己的可变参数函数。

定义可变参数函数


我们可以通过在参数前面使用省略号 (...) 来定义可变参数函数。 让我们创建一个程序,当人们的名字被发送到函数时打招呼:

hello.go

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

我们创建了一个 sayHello 函数,它只接受一个名为 names 的参数。 该参数是可变参数,因为我们在数据类型之前放置了一个省略号 (...):...string。 这告诉 Go 函数可以接受零个、一个或多个参数。

sayHello 函数接收名称参数作为切片。 由于数据类型是字符串,因此可以将 names 参数视为函数体内的字符串切片 ([]string)。 我们可以使用范围运算符创建一个循环并遍历字符串切片。

如果我们运行程序,我们将得到以下输出:

Output
Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

请注意,我们第一次调用 sayHello 时没有打印任何内容。 这是因为可变参数是一个空的字符串切片。 由于我们正在遍历切片,因此没有什么可以迭代,并且 fmt.Printf 永远不会被调用。

让我们修改程序以检测没有发送任何值:

hello.go

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    if len(names) == 0 {
        fmt.Println("nobody to greet")
        return
    }
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

现在,通过使用 if 语句,如果没有传递任何值,则名称的长度将为 0,我们将打印出没有人来打招呼:

Output
nobody to greet
Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie


使用可变参数可以使您的代码更具可读性。 让我们创建一个函数,将单词与指定的分隔符连接在一起。 我们将首先创建没有可变参数函数的程序来展示它的读取方式:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
    fmt.Println(line)

    line = join(",", []string{"Sammy", "Jessica"})
    fmt.Println(line)

    line = join(",", []string{"Sammy"})
    fmt.Println(line)
}

func join(del string, values []string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

在这个程序中,我们将逗号 (,) 作为分隔符传递给连接函数。 然后我们传递一个值来加入。 这是输出:

Output
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy


因为该函数将字符串切片作为值参数,所以当我们调用连接函数时,我们必须将所有单词包装在切片中。 这会使代码难以阅读。

现在,让我们编写相同的函数,但我们将使用可变参数函数:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

如果我们运行该程序,我们可以看到我们得到了与之前的程序相同的输出:

Output
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy


虽然 join 函数的两个版本都以编程方式执行完全相同的操作,但函数的可变参数版本在调用时更容易阅读。

可变参数顺序


一个函数中只能有一个可变参数,并且它必须是函数中定义的最后一个参数。 以除最后一个参数以外的任何顺序在可变参数函数中定义参数将导致编译错误:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

func join(values ...string, del string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

这次我们将 values 参数放在 join 函数中。 这将导致以下编译错误:

Output
./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values


定义任何可变参数函数时,只有最后一个参数可以是可变参数。

展开的参数


到目前为止,我们已经看到我们可以将零、一个或多个值传递给可变参数函数。 但是,有时我们有一个值切片并且我们希望将它们发送到可变参数函数。

让我们看看上一节中的 join 函数,看看会发生什么:

join.go

package main

import "fmt"

func main() {
    var line string

    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

    line = join(",", names)
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

如果我们运行这个程序,我们会收到一个编译错误:




Output

./join-error.go:10:14: cannot use names (type []string) as type string in argument to join


尽管可变参数函数会将 values ...string 的参数转换为字符串切片 []string,但我们不能将字符串切片作为参数传递。 这是因为编译器需要字符串的离散参数。

为了解决这个问题,我们可以通过在切片后面加上一组省略号 (...) 来分解切片,并将其转换为将传递给可变参数函数的离散参数:

join.go

package main

import "fmt"

func main() {
    var line string

    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

    line = join(",", names...)
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

这一次,当我们调用连接函数时,我们通过附加省略号 (...) 来分解名称切片。

这允许程序现在按预期运行:

Output
Sammy,Jessica,Drew,Jamie


需要注意的是,我们仍然可以传递零个、一个或多个参数,以及我们分解的切片。 以下是我们迄今为止看到的所有变体的代码:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)

}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

 

Output
Sammy,Jessica,Drew,Jamie
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

 

我们现在知道如何将零个、一个或多个参数以及我们分解的切片传递给可变参数函数。

结论


在本教程中,我们了解了可变参数函数如何使您的代码更简洁。 虽然您并不总是需要使用它们,但您可能会发现它们很有用:

  • 如果你发现你正在创建一个临时切片只是为了传递给一个函数。
  • 当输入参数的数量未知或调用时会发生变化。
  • 使您的代码更具可读性。
文章链接