【Go-4】函數

函數

函數是編程中的基本構建塊,用于封裝可重用的代碼邏輯。Go語言中的函數功能強大,支持多種特性,如多返回值、可變參數、匿名函數、閉包以及將函數作為值和類型傳遞。理解和掌握函數的使用對于編寫高效、可維護的Go程序至關重要。本章將詳細介紹Go語言中的函數,包括函數的定義與調用、參數和返回值、可變參數函數、匿名函數與閉包,以及函數作為值和類型的應用。

4.1 函數定義與調用

函數的基本定義

在Go語言中,函數使用func關鍵字定義。函數可以包含參數和返回值,也可以沒有。

基本語法:

func 函數名(參數列表) (返回值列表) {// 函數體
}
  • func: 函數定義的關鍵字。
  • 函數名: 函數的名稱,遵循標識符命名規則。
  • 參數列表: 函數接受的參數,可以有多個,每個參數需要指定類型。
  • 返回值列表: 函數返回的值,可以有多個,需指定類型。
  • 函數體: 包含函數執行的代碼。

示例:

package mainimport "fmt"// 定義一個簡單的函數,不接受參數,也不返回值
func sayHello() {fmt.Println("Hello, Go!")
}func main() {sayHello() // 調用函數
}

輸出:

Hello, Go!
帶參數的函數

函數可以接受多個參數,每個參數需要指定類型。參數之間用逗號分隔。

示例:

package mainimport "fmt"// 定義一個函數,接受兩個整數參數并打印它們的和
func add(a int, b int) {sum := a + bfmt.Println("Sum:", sum)
}func main() {add(5, 3) // 調用函數,傳遞參數5和3
}

輸出:

Sum: 8

參數類型簡寫:

當連續的參數具有相同的類型時,可以簡化參數類型的聲明。

示例:

package mainimport "fmt"// 簡化參數類型聲明
func multiply(a, b int) {product := a * bfmt.Println("Product:", product)
}func main() {multiply(4, 6) // 調用函數,傳遞參數4和6
}

輸出:

Product: 24
帶返回值的函數

函數可以返回一個或多個值。返回值需要在函數定義中指定。

示例:

package mainimport "fmt"// 定義一個函數,接受兩個整數參數并返回它們的和
func add(a int, b int) int {return a + b
}func main() {sum := add(10, 15) // 調用函數,并接收返回值fmt.Println("Sum:", sum)
}

輸出:

Sum: 25
多返回值函數

Go語言支持函數返回多個值,這在錯誤處理和復雜數據返回時非常有用。

示例:

package mainimport ("fmt""math"
)// 定義一個函數,返回兩個值:平方根和平方
func calculate(x float64) (float64, float64) {sqrt := math.Sqrt(x)square := x * xreturn sqrt, square
}func main() {number := 16.0sqrt, square := calculate(number) // 接收多個返回值fmt.Printf("Number: %.2f, Square Root: %.2f, Square: %.2f\n", number, sqrt, square)
}

輸出:

Number: 16.00, Square Root: 4.00, Square: 256.00
命名返回值

函數的返回值可以命名,這樣在函數體內可以直接使用這些名字,并且可以使用return語句直接返回。

示例:

package mainimport "fmt"// 定義一個函數,返回兩個命名的返回值
func divide(a, b float64) (quotient float64, remainder float64) {quotient = a / bremainder = math.Mod(a, b)return // 自動返回命名的返回值
}func main() {q, r := divide(10.5, 3.2)fmt.Printf("Quotient: %.2f, Remainder: %.2f\n", q, r)
}

輸出:

Quotient: 3.28, Remainder: 0.90

4.2 函數參數和返回值

函數參數和返回值是函數與外界交互的主要方式。Go語言在參數傳遞和返回值處理上有其獨特的特性。

參數傳遞方式

Go語言中的參數傳遞是按值傳遞,這意味著函數接收到的是參數的副本,對副本的修改不會影響原始變量。

示例:

package mainimport "fmt"// 定義一個函數,嘗試修改參數的值
func modifyValue(x int) {x = 100fmt.Println("Inside modifyValue:", x)
}func main() {a := 50modifyValue(a)fmt.Println("After modifyValue:", a) // a的值不會被修改
}

輸出:

Inside modifyValue: 100
After modifyValue: 50
使用指針傳遞參數

為了在函數內部修改外部變量的值,可以使用指針傳遞參數。指針傳遞允許函數直接訪問和修改變量的內存地址。

示例:

package mainimport "fmt"// 定義一個函數,使用指針修改參數的值
func modifyPointer(x *int) {*x = 100fmt.Println("Inside modifyPointer:", *x)
}func main() {a := 50fmt.Println("Before modifyPointer:", a)modifyPointer(&a) // 傳遞變量a的地址fmt.Println("After modifyPointer:", a) // a的值被修改
}

輸出:

Before modifyPointer: 50
Inside modifyPointer: 100
After modifyPointer: 100
多參數函數

Go語言支持多個參數的函數,可以組合使用不同類型的參數。

示例:

package mainimport "fmt"// 定義一個函數,接受多個不同類型的參數
func printDetails(name string, age int, height float64) {fmt.Printf("Name: %s, Age: %d, Height: %.2f\n", name, age, height)
}func main() {printDetails("Alice", 30, 5.6)printDetails("Bob", 25, 5.9)
}

輸出:

Name: Alice, Age: 30, Height: 5.60
Name: Bob, Age: 25, Height: 5.90
可選參數

Go語言不直接支持可選參數,但可以通過參數的組合和使用指針來模擬實現。

示例:

package mainimport "fmt"// 定義一個函數,使用指針模擬可選參數
func greet(name string, title *string) {if title != nil {fmt.Printf("Hello, %s %s!\n", *title, name)} else {fmt.Printf("Hello, %s!\n", name)}
}func main() {var title string = "Dr."greet("Alice", &title) // 使用標題greet("Bob", nil)      // 不使用標題
}

輸出:

Hello, Dr. Alice!
Hello, Bob!

4.3 可變參數函數

可變參數函數允許函數接受任意數量的參數。這在處理不確定數量輸入時非常有用。Go語言通過在參數類型前加...來定義可變參數。

定義可變參數函數

基本語法:

func 函數名(參數類型, ...參數類型) 返回值類型 {// 函數體
}

示例:

package mainimport "fmt"// 定義一個函數,接受可變數量的整數參數并求和
func sum(nums ...int) int {total := 0for _, num := range nums {total += num}return total
}func main() {fmt.Println(sum(1, 2, 3))       // 輸出: 6fmt.Println(sum(10, 20, 30, 40)) // 輸出: 100fmt.Println(sum())              // 輸出: 0
}

輸出:

6
100
0
使用可變參數的其他示例

示例1:打印多個字符串

package mainimport "fmt"// 定義一個函數,接受可變數量的字符串參數并打印
func printStrings(strs ...string) {for _, s := range strs {fmt.Println(s)}
}func main() {printStrings("Go", "is", "fun")printStrings("Hello", "World")printStrings()
}

輸出:

Go
is
fun
Hello
World

示例2:計算多個浮點數的平均值

package mainimport "fmt"// 定義一個函數,接受可變數量的浮點數參數并計算平均值
func average(nums ...float64) float64 {if len(nums) == 0 {return 0}total := 0.0for _, num := range nums {total += num}return total / float64(len(nums))
}func main() {fmt.Printf("Average: %.2f\n", average(1.5, 2.5, 3.5))       // 輸出: Average: 2.50fmt.Printf("Average: %.2f\n", average(10.0, 20.0))         // 輸出: Average: 15.00fmt.Printf("Average: %.2f\n", average())                  // 輸出: Average: 0.00
}

輸出:

Average: 2.50
Average: 15.00
Average: 0.00
將切片傳遞給可變參數函數

如果已經有一個切片,可以使用...操作符將切片元素作為可變參數傳遞給函數。

示例:

package mainimport "fmt"// 定義一個函數,接受可變數量的整數參數并求和
func sum(nums ...int) int {total := 0for _, num := range nums {total += num}return total
}func main() {numbers := []int{4, 5, 6}total := sum(numbers...) // 使用...將切片傳遞為可變參數fmt.Println("Total:", total) // 輸出: Total: 15
}

輸出:

Total: 15

4.4 匿名函數與閉包

匿名函數

匿名函數是沒有名稱的函數,可以在定義時直接調用,或賦值給變量以便后續使用。匿名函數在需要臨時使用函數邏輯時非常有用。

示例1:立即調用匿名函數

package mainimport "fmt"func main() {// 定義并立即調用匿名函數func() {fmt.Println("This is an anonymous function!")}()// 帶參數的匿名函數func(a, b int) {fmt.Printf("Sum: %d\n", a+b)}(3, 4)
}

輸出:

This is an anonymous function!
Sum: 7

示例2:將匿名函數賦值給變量

package mainimport "fmt"func main() {// 將匿名函數賦值給變量greet := func(name string) {fmt.Printf("Hello, %s!\n", name)}greet("Alice")greet("Bob")
}

輸出:

Hello, Alice!
Hello, Bob!
閉包

閉包是指一個函數可以訪問其外部作用域中的變量,即使外部函數已經返回。閉包允許函數“記住”并操作其定義時的環境變量。

示例1:簡單閉包

package mainimport "fmt"// 定義一個生成器函數,返回一個閉包
func generator() func() int {count := 0return func() int {count++return count}
}func main() {next := generator()fmt.Println(next()) // 輸出: 1fmt.Println(next()) // 輸出: 2fmt.Println(next()) // 輸出: 3another := generator()fmt.Println(another()) // 輸出: 1
}

輸出:

1
2
3
1

解釋:

  • generator函數返回一個匿名函數,該匿名函數訪問并修改外部變量count
  • 每次調用next()時,count都會遞增。
  • another是另一個閉包實例,擁有獨立的count變量。

示例2:閉包與參數

package mainimport "fmt"// 定義一個函數,返回一個閉包,該閉包會將輸入乘以指定的因子
func multiplier(factor int) func(int) int {return func(x int) int {return x * factor}
}func main() {double := multiplier(2)triple := multiplier(3)fmt.Println("Double 5:", double(5)) // 輸出: Double 5: 10fmt.Println("Triple 5:", triple(5)) // 輸出: Triple 5: 15
}

輸出:

Double 5: 10
Triple 5: 15

解釋:

  • multiplier函數接受一個factor參數,并返回一個閉包。
  • 該閉包接受一個整數x,并返回x乘以factor的結果。
  • doubletriple是兩個不同的閉包實例,分別將輸入數值乘以2和3。
閉包的應用場景
  • 延遲執行:將某些操作延遲到特定條件下執行。
  • 數據封裝:封裝數據,保護數據不被外部直接修改。
  • 回調函數:作為回調函數傳遞給其他函數,以實現靈活的功能擴展。

示例:延遲執行

package mainimport "fmt"// 定義一個函數,接受一個函數作為參數
func performOperation(operation func()) {fmt.Println("準備執行操作...")operation()fmt.Println("操作執行完畢。")
}func main() {performOperation(func() {fmt.Println("這是一個延遲執行的匿名函數。")})
}

輸出:

準備執行操作...
這是一個延遲執行的匿名函數。
操作執行完畢。

4.5 函數作為值和類型

在Go語言中,函數可以作為值來傳遞和使用。這意味著函數可以被賦值給變量、作為參數傳遞給其他函數,甚至作為返回值返回。這種特性使得Go語言在函數式編程方面具有很大的靈活性。

將函數賦值給變量

函數可以被賦值給變量,從而實現對函數的引用和調用。

示例:

package mainimport "fmt"// 定義一個簡單的函數
func sayHello() {fmt.Println("Hello!")
}func main() {// 將函數賦值給變量greeting := sayHello// 調用通過變量引用的函數greeting() // 輸出: Hello!
}

輸出:

Hello!
將函數作為參數傳遞

函數可以作為參數傳遞給其他函數,允許更高層次的抽象和代碼復用。

示例:

package mainimport "fmt"// 定義一個函數類型
type operation func(int, int) int// 定義一個函數,接受另一個函數作為參數
func compute(a int, b int, op operation) int {return op(a, b)
}// 定義具體的操作函數
func add(a int, b int) int {return a + b
}func multiply(a int, b int) int {return a * b
}func main() {sum := compute(5, 3, add)product := compute(5, 3, multiply)fmt.Println("Sum:", sum)         // 輸出: Sum: 8fmt.Println("Product:", product) // 輸出: Product: 15
}

輸出:

Sum: 8
Product: 15

解釋:

  • operation是一個函數類型,接受兩個整數并返回一個整數。
  • compute函數接受兩個整數和一個operation類型的函數作為參數,并返回操作結果。
  • addmultiply是具體的操作函數,分別實現加法和乘法。
將函數作為返回值

函數可以作為其他函數的返回值,允許動態生成函數或實現高階函數的功能。

示例:

package mainimport "fmt"// 定義一個函數,返回一個函數,該返回函數會將輸入數值加上指定的值
func adder(x int) func(int) int {return func(y int) int {return x + y}
}func main() {addFive := adder(5)addTen := adder(10)fmt.Println("5 + 3 =", addFive(3))  // 輸出: 5 + 3 = 8fmt.Println("10 + 7 =", addTen(7))  // 輸出: 10 + 7 = 17
}

輸出:

5 + 3 = 8
10 + 7 = 17

解釋:

  • adder函數接受一個整數x,并返回一個匿名函數,該匿名函數接受另一個整數y,返回x + y的結果。
  • addFiveaddTen分別是不同的閉包實例,綁定了不同的x值。
使用函數作為數據結構的元素

函數可以被存儲在數據結構中,如切片、Map等,提供更高的靈活性和擴展性。

示例1:將函數存儲在切片中

package mainimport "fmt"// 定義一個函數類型
type operation func(int, int) intfunc main() {// 創建一個存儲函數的切片operations := []operation{func(a, b int) int { return a + b },func(a, b int) int { return a - b },func(a, b int) int { return a * b },func(a, b int) int { return a / b },}a, b := 20, 5for _, op := range operations {result := op(a, b)fmt.Println(result)}
}

輸出:

25
15
100
4

示例2:將函數存儲在Map中

package mainimport "fmt"// 定義一個函數類型
type operation func(int, int) intfunc main() {// 創建一個存儲函數的Mapoperations := map[string]operation{"add":      func(a, b int) int { return a + b },"subtract": func(a, b int) int { return a - b },"multiply": func(a, b int) int { return a * b },"divide":   func(a, b int) int { return a / b },}a, b := 15, 3for name, op := range operations {result := op(a, b)fmt.Printf("%s: %d\n", name, result)}
}

輸出:

add: 18
subtract: 12
multiply: 45
divide: 5

解釋:

  • 在第一個示例中,函數被存儲在切片中,可以通過索引訪問和調用。
  • 在第二個示例中,函數被存儲在Map中,通過鍵名訪問和調用,提供更具語義化的調用方式。
函數作為接口的實現

Go語言中的接口類型可以包含函數類型,使得接口的實現更加靈活。

示例:

package mainimport "fmt"// 定義一個接口,包含一個函數方法
type Greeter interface {Greet(name string) string
}// 定義一個結構體,實現Greeter接口
type Person struct {greeting string
}// 實現Greet方法
func (p Person) Greet(name string) string {return fmt.Sprintf("%s, %s!", p.greeting, name)
}func main() {var greeter Greetergreeter = Person{greeting: "Hello"}message := greeter.Greet("Alice")fmt.Println(message) // 輸出: Hello, Alice!
}

輸出:

Hello, Alice!

解釋:

  • Greeter接口定義了一個Greet方法。
  • Person結構體實現了Greet方法,從而滿足Greeter接口。
  • 通過接口類型變量greeter可以調用具體實現的Greet方法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/906726.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/906726.shtml
英文地址,請注明出處:http://en.pswp.cn/news/906726.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【已解決】HBuilder X編輯器在外接顯示器或者4K顯示器怎么界面變的好小問題

觸發方式:主要涉及DPI縮放問題,可能在電腦息屏有概率觸發 修復方式: 1.先關掉軟件直接更改屏幕縮放,然后打開軟件,再關掉軟件恢復原來的縮放,再打開軟件就好了 2.(不推薦)右鍵HBuilder在屬性里…

spark調度系統核心組件SparkContext、DAGSchedul、TaskScheduler、Taskset介紹

目錄 1. SparkContext2.DAGScheduler3. TaskScheduler4. 協作關系5 TaskSet的定義6. 組件關系說明Spark調度系統的核心組件主要有SparkContext、DAGScheduler和TaskScheduler SparkContext介紹 1. SparkContext 1、資源申請: SparkContext是Spark應用程序與集群管理器(如St…

VSCode+EIDE通過KeilC51編譯,使VSCode+EIDE“支持”C和ASM混編

在使用Keil C51時,要讓Keil C51支持混編則需要在混編的.c文件上右鍵選擇Options for File *(ALTF7),打開選項界面后,在 Properties 頁 勾上 Generate Assembler SRC File 和 Assemble SRC File ,如下圖所示: 這樣設置后…

SQLynx:一款跨平臺的企業級數據庫管理工具

SQLynx 是一款支持跨平臺(Windows、Linux、macOS、Web)的企業級數據庫管理和 SQL 工具,可以提供高效、安全且適配國產化技術棧的數據庫管理解決方案。 數據源 SQLynx 支持連接各種關系型數據庫、非關系型數據庫以及大數據平臺,包…

實戰項目8(實訓)

目錄 項目01 【sw1】配置 【sw2】配置 任務結果截圖 項目02 【sw1】配置 【sw2】配置 任務結果截圖 項目03 【sw1】配置 任務結果截圖 項目04 【sw1】配置 【r1】配置 任務結果截圖 項目05 【r1】配置 【r2】配置 【r3】配置 任務結果截圖 項目06 【r1】…

TCP為什么是三次握手,而不是二次?

為什么需要三次握手? 想象一下,你要給遠方的朋友寄一份重要文件。你會怎么做? 普通人的做法: 直接扔進郵箱,祈禱別丟了 聰明人的做法: 先打電話確認地址,再發快遞,最后確認收到 T…

dubbo使用nacos作為注冊中心配置

<dubbo:registry protocol"nacos" address"${dubbo.registry.address.nacos}" /> <dubbo:metadata-report address"${dubbo.metadata-report.address}"/> 如果有多個地址&#xff0c;這塊如何配置呢&#xff1f; nacos://ip:端口?…

教師角色的轉變:從知識傳授者到學習引導者

教師角色的轉變&#xff1a;從知識傳授者到學習引導者 隨著人工智能&#xff08;AI&#xff09;和信息技術的迅速發展&#xff0c;教育正在經歷深刻的變革。其中&#xff0c;教師角色的轉變尤為關鍵。傳統上&#xff0c;教師主要承擔“知識傳授者”的職責&#xff0c;即向學生…

PostgreSQL 用戶權限與安全管理

1 系統默認角色 postgres# select rolname from pg_roles; rolname ----------------------------- postgres pg_database_owner pg_read_all_data pg_write_all_data pg_monitor pg_read_all_settings pg_read_all_stats pg_stat_scan_tables …

C++構造函數和析構函數

C++構造函數和析構函數 C++的構造函數和析構函數是類的特殊成員函數,用于對象的創建和銷毀,分別在對象的生命周期開始和結束時自動調用。它們的使用對資源管理和對象的初始化/清理至關重要。 1. 構造函數 定義 構造函數在對象創建時自動調用,用于初始化對象的數據成員。構造…

根據Cortex-M3(STM32F1)權威指南講解MCU內存架構與如何查看編譯器生成的地址具體位置

首先我們先查看官方對于Cortex-M3預定義的存儲器映射 1.存儲器映射 1.1 Cortex-M3架構的存儲器結構 內部私有外設總線&#xff1a;即AHB總線&#xff0c;包括NVIC中斷&#xff0c;ITM硬件調試&#xff0c;FPB, DWT。 外部私有外設總線&#xff1a;即APB總線&#xff0c;用于…

軟件設計師“測試用例”考點分析——求三連

一、測試用例設計核心要點解析 1. 白盒測試覆蓋標準 &#xff08;1&#xff09;路徑覆蓋&#xff1a;需覆蓋程序中所有可能的路徑。如2018年真題路徑覆蓋需要3組測試用例&#xff08;①②、①③、①③④&#xff09;&#xff0c;2020年流程圖則需4個用例覆蓋ace/abd/abe/acd四…

Linux 用戶無法遠程連接服務器

前言 昨天深夜一點多接到客戶電話&#xff0c;客戶說OS用戶下午下班前還能正常登錄。因為晚上一點半需要關閉所有服務進行遷移&#xff0c;但是用戶無法登錄了&#xff0c;導致后續流程無法執行。我讓他先通過root用戶緊急修改了密碼&#xff0c;先保證業務正常流轉。 問題 …

多模態大語言模型arxiv論文略讀(八十八)

MammothModa: Multi-Modal Large Language Model ?? 論文標題&#xff1a;MammothModa: Multi-Modal Large Language Model ?? 論文作者&#xff1a;Qi She, Junwen Pan, Xin Wan, Rui Zhang, Dawei Lu, Kai Huang ?? 研究機構: ByteDance, Beijing, China ?? 問題背景…

svn遷移到git保留記錄和Python字符串格式化 f-string的進化歷程

svn遷移到git保留記錄 and Python字符串格式化(二&#xff09;&#xff1a; f-string的進化歷程 在將項目從SVN遷移到Git時&#xff0c;保留完整的版本歷史記錄非常重要。下面是詳細的步驟和工具&#xff0c;可以幫助你完成這一過程&#xff1a; 安裝Git和SVN工具 首先&#…

springboot配置mysql druid連接池,以及連接池參數解釋

文章目錄 前置配置方式參數解釋 前置 springboot 項目javamysqldruid 連接池 配置方式 在 springboot 的 application.yml 中配置基本方式 # Druid 配置&#xff08;Spring Boot YAML 格式&#xff09; spring:datasource:url: jdbc:mysql://localhost:3306/testdb?useSSL…

vue實現高亮文字效果——advanced-mark.js

組件介紹-advanced-mark.js&#xff1a; advanced-mark.js 是一個用于 Vue 的高亮文字組件&#xff0c;它可以幫助你在文本中高亮顯示指定的關鍵詞或短語。 組件地址&#xff1a;https://angezid.github.io/advanced-mark.js/doc-v2/getting-started.html 主要功能&#xff1…

DC30V/2.5A同步降壓芯片SL1581 輸入24V降壓5V 12V2A電流

在工業自動化、汽車電子等領域&#xff0c;24V 電源系統向 5V/12V 雙軌供電的需求日益增長。針對這一痛點&#xff0c;森利威爾電子重磅推出 DC30V/2.5A 同步降壓芯片 SL1581&#xff0c;憑借卓越的性能和創新設計&#xff0c;為工程師提供高可靠性、高性價比的電源解決方案。 …

React 第四十四節Router中 usefetcher的使用詳解及注意事項

前言 useFetcher 是 React Router 中一個強大的鉤子&#xff0c;用于在不觸發頁面導航的情況下執行數據加載&#xff08;GET&#xff09;或提交&#xff08;POST&#xff09;。 一、useFetcher 應用場景&#xff1a; 1、后臺數據預加載&#xff08;如鼠標懸停時加載數據&…

Jmeter(三) - 測試計劃(Test Plan)的元件

1.簡介 上一篇已經教你如何通過JMeter來創建一個測試計劃&#xff08;Test Plan&#xff09;&#xff0c;那么這一篇我們就將JMeter啟動起來&#xff0c;創建一個測試計劃&#xff08;Test plan&#xff09;&#xff0c;給大家介紹一下測試計劃&#xff08;Test Plan&#xff…