通过示例学习-Go-语言-2023-四-
通过示例学习 Go 语言 2023(四)
Go 语言中的二叉搜索树
目录
** 介绍
- 完整工作代码
介绍
二叉搜索树(BST)是一个二叉树。每个节点在二叉搜索树中
-
左子树中每个节点的值都小于当前节点的值
-
右子树中每个节点的值都大于当前节点的值
-
左右子树本身都是二叉搜索树
完整工作代码
package main
import "fmt"
type bstnode struct {
value int
left *bstnode
right *bstnode
}
type bst struct {
root *bstnode
}
func (b *bst) reset() {
b.root = nil
}
func (b *bst) insert(value int) {
b.insertRec(b.root, value)
}
func (b *bst) insertRec(node *bstnode, value int) *bstnode {
if b.root == nil {
b.root = &bstnode{
value: value,
}
return b.root
}
if node == nil {
return &bstnode{value: value}
}
if value <= node.value {
node.left = b.insertRec(node.left, value)
}
if value > node.value {
node.right = b.insertRec(node.right, value)
}
return node
}
func (b *bst) find(value int) error {
node := b.findRec(b.root, value)
if node == nil {
return fmt.Errorf("Value: %d not found in tree", value)
}
return nil
}
func (b *bst) findRec(node *bstnode, value int) *bstnode {
if node == nil {
return nil
}
if node.value == value {
return b.root
}
if value < node.value {
return b.findRec(node.left, value)
}
return b.findRec(node.right, value)
}
func (b *bst) inorder() {
b.inorderRec(b.root)
}
func (b *bst) inorderRec(node *bstnode) {
if node != nil {
b.inorderRec(node.left)
fmt.Println(node.value)
b.inorderRec(node.right)
}
}
func main() {
bst := &bst{}
eg := []int{2, 5, 7, -1, -1, 5, 5}
for _, val := range eg {
bst.insert(val)
}
fmt.Printf("Printing Inorder:\n")
bst.inorder()
bst.reset()
eg = []int{4, 5, 7, 6, -1, 99, 5}
for _, val := range eg {
bst.insert(val)
}
fmt.Printf("\nPrinting Inorder:\n")
bst.inorder()
fmt.Printf("\nFinding Values:\n")
err := bst.find(2)
if err != nil {
fmt.Printf("Value %d Not Found\n", 2)
} else {
fmt.Printf("Value %d Found\n", 2)
}
err = bst.find(6)
if err != nil {
fmt.Printf("Value %d Not Found\n", 6)
} else {
fmt.Printf("Value %d Found\n", 6)
}
err = bst.find(5)
if err != nil {
fmt.Printf("Value %d Not Found\n", 5)
} else {
fmt.Printf("Value %d Found\n", 5)
}
err = bst.find(1)
if err != nil {
fmt.Printf("Value %d Not Found\n", 1)
} else {
fmt.Printf("Value %d Found\n", 1)
}
}
输出:
Printing Inorder:
-1
-1
2
5
5
5
7
Printing Inorder:
-1
4
5
5
6
7
99
Finding Values:
Value 2 Not Found
Value 6 Found
Value 5 Found
Value 1 Not Found
Go (Golang) 中的二叉树最大路径和程序
目录
-
概述
-
程序
概述
给定一个二叉树。目标是找到该二叉树中的最大路径和。二叉树中的一条路径是相互连接的节点序列。每个节点在最大路径和中仅出现一次。
示例 1
Output: 16
Maximum Sum Path is: 4->2->1->3->6
示例 2
Output: 14
Maximum Sum Path is: 5->3->6
这个思路是跟踪每个节点下面的四个值
-
a = root.Val
-
b = root.Val + leftSubTreeMaxSum
-
c = root.Val + rightSubTreeMaxSum
-
d = root.Val + leftSubTreeMaxSum + rightSubTreeMaxSum
然后
-
给定节点的最大和是 (a,b,c,d) 的最大值
-
递归调用中的返回值将是 (a,b,c) 的最大值。为什么?这是因为只有 a、b 或 c 的路径代表可以在父节点中考虑的路径。d 不能被考虑,因为它成为一个无效路径。为了更好理解这一点,请考虑上面示例二中的二叉树。路径 5->3->6 不能包含父节点 -5,因为这会变成一个无效路径。
程序
下面是相同的程序
package main
import (
"fmt"
"math"
)
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func maxPathSum(root *TreeNode) int {
res := math.MinInt64
maxPathSumUtil(root, &res)
return res
}
func maxPathSumUtil(root *TreeNode, res *int) int {
if root == nil {
return 0
}
l := maxPathSumUtil(root.Left, res)
r := maxPathSumUtil(root.Right, res)
a := root.Val
b := root.Val + l
c := root.Val + r
d := root.Val + l + r
maxReturnSum := maxOfThree(a, b, c)
maxSumPath := maxOfTwo(maxReturnSum, d)
if maxSumPath > *res {
*res = maxSumPath
}
return maxReturnSum
}
func maxOfThree(a, b, c int) int {
if a > b && a > c {
return a
}
if b > c {
return b
}
return c
}
func maxOfTwo(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
root := &TreeNode{Val: 1}
root.Left = &TreeNode{Val: 2}
root.Left.Left = &TreeNode{Val: 4}
root.Right = &TreeNode{Val: 3}
root.Right.Left = &TreeNode{Val: 5}
root.Right.Right = &TreeNode{Val: 6}
output := maxPathSum(root)
fmt.Println(output)
root = &TreeNode{Val: -10}
root.Left = &TreeNode{Val: 2}
root.Left.Left = &TreeNode{Val: 4}
root.Right = &TreeNode{Val: 3}
root.Right.Left = &TreeNode{Val: 5}
root.Right.Right = &TreeNode{Val: 6}
output = maxPathSum(root)
fmt.Println(output)
}
输出:
16
14
注意: 请查看我们的 Golang 高级教程。本系列教程详细而全面,我们努力用示例覆盖所有概念。本教程适合那些希望获得专业知识和对 golang 有深入理解的人 – Golang 高级教程
如果你对了解所有设计模式在 Golang 中如何实现感兴趣。如果是,那么这篇文章适合你 – 所有设计模式 Golang
此外,请查看我们的系统设计教程系列 – 系统设计教程系列
Go (Golang) 中的空标识符导入。
导入包中的空标识符意味着为导入的包指定空导入。其语法为:
import _ <directory_path></directory_path>
什么是这个空导入,为什么要使用它。为此你需要理解两件事:
-
关于 init 函数。
-
关于由下划线(‘_‘)表示的空标识符。
init() 函数。
init() 函数是一个特殊的函数,用于初始化包的全局变量。这些函数在包初始化时执行。包中的每个 GO 源文件可以有自己的 init() 函数。每当你在程序中导入任何包时,程序执行时,将首先调用属于该导入包的 GO 源文件中的 init 函数(如果存在)。关于 init 函数需要注意的一些要点:
-
Init 函数是可选的。
-
Init 函数不接受任何参数。
-
Init 函数没有任何返回值。
-
Init 函数是隐式调用的。由于它是隐式调用的,init 函数无法从任何地方引用。
-
在同一个源文件中可以有多个 init() 函数。
关于空标识符。
Go 不允许任何未使用的变量。任何未使用的变量可以被空标识符(‘_’)替代。
所以现在,当使用一个包的空导入时,
-
当前程序中未使用导入的包。
-
但我们打算导入该包,以便可以正确调用属于该包的 GO 源文件中的 init 函数,并进行变量的初始化。
所以基本上,当一个包仅仅为了其副作用被导入时,就使用空导入。
例如,mysql 包作为空导入,用于其副作用,即在 mysql 包的 init() 函数中注册 mysql 驱动程序作为数据库驱动程序,而不导入任何其他函数:
_ "github.com/go-sql-driver/mysql"
要使用任何 mysql 库,例如 gorm 或 xorm,上述空导入是必要的。
Go (Golang)中的布尔常量
目录
概述
-
示例
-
类型布尔常量
-
未类型命名布尔常量
-
未类型未命名布尔常量
-
概述
有两个未类型的布尔常量true和false。下面是一个说明布尔常量的程序。
为了更好地理解 Golang 中的布尔常量,了解 Go 中的类型和未类型常量是很重要的。请参考这篇文章 – golangbyexample.com/typed-untyped-constant-golang
/
一旦你读完这篇文章,你就会明白常量可以以三种方式声明。
-
类型常量
-
未类型未命名常量
-
未类型命名常量
布尔常量的情况也是如此。让我们看看一个程序来理解它。
示例
package main
import "fmt"
func main() {
type myBool bool
//Typed Boolean constant
const aa bool = true
var uu = aa
fmt.Println("Typed named boolean constant")
fmt.Printf("uu: Type: %T Value: %v\n\n", uu, uu)
//Below line will raise a compilation error
//var vv myBool = aa
//Untyped named boolean constant
const bb = true
var ww myBool = bb
var xx = bb
fmt.Println("Untyped named boolean constant")
fmt.Printf("ww: Type: %T Value: %v\n", ww, ww)
fmt.Printf("xx: Type: %T Value: %v\n\n", xx, xx)
//Untyped unnamed boolean constant
var yy myBool = true
var zz = true
fmt.Println("Untyped unnamed boolean constant")
fmt.Printf("yy: Type: %T Value: %v\n", yy, yy)
fmt.Printf("zz: Type: %T Value: %v\n", zz, zz)
}
输出:
Typed named boolean constant
uu: Type: bool Value: true
Untyped named boolean constant
ww: Type: main.myBool Value: true
xx: Type: bool Value: true
Untyped unnamed boolean constant
yy: Type: main.myBool Value: true
zz: Type: bool Value: true
在上述程序中,我们创建了一个新类型myBool。
type myBool bool
以上程序还展示了
-
类型布尔常量
-
未类型未命名布尔常量
-
未类型命名布尔常量
让我们理解它们及其行为
类型布尔常量
它的定义如下
const aa bool = true
上述注意到,以下代码行将导致编译错误。这是因为变量aa是类型为boolean的常量。因此,以下代码行会导致编译错误,因为它无法被赋值给类型为myBool的变量。
var v mybool = aa
但类型字符串常量可以像下面那样赋值给用var关键字创建的变量。
var uu = aa
未类型命名布尔常量
它的定义如下
const bb = true
未类型命名字符串常量可以赋值给类型为myBool的变量以及用var关键字创建的变量,因为它是未类型的,因此常量的类型将根据赋值给的变量类型决定。
var ww mybool = bb
var xx = bb
未类型未命名布尔常量
它类似于下面的情况。
true
未类型未命名字符串常量可以赋值给类型为myBool的变量以及用var关键字创建的变量,因为它是未类型的,因此常量的类型将根据赋值给的变量类型决定。
var yy mybool = true
var zz = true
在 Go 语言中将数字拆分为整数和小数部分
目录
-
概述
-
代码
概述
GO 的math包提供了一个Modf方法,可以用来将浮点数拆分为整数部分和浮动部分。请注意,该函数也将整数部分作为浮点数返回。
以下是该函数的签名。它接受一个浮点数作为输入并返回两个 float64。第一个是整数部分,第二个是小数部分。
func Modf(f float64) (int float64, frac float64)
关于上述函数的一些注意事项
-
返回值int和frac加起来等于输入f
-
int和frac与输入f具有相同的符号
另外,Modf函数的一些特殊情况是
-
Modf(±Inf) = ±Inf, NaN
-
Modf(NaN) = NaN, NaN
代码
package main
import (
"fmt"
"math"
)
func main() {
//Contain both integer and fraction
integer, fraction := math.Modf(2.5)
fmt.Printf("Integer: %f. Fraction: %f\n", integer, fraction)
//Contain only integer part
integer, fraction = math.Modf(2)
fmt.Printf("Integer: %f. Fraction: %f\n", integer, fraction)
//Negative floating point number
integer, fraction = math.Modf(-2.5)
fmt.Printf("Integer: %f. Fraction: %f\n", integer, fraction)
//Contains only fraction part
integer, fraction = math.Modf(0.5)
fmt.Printf("Integer: %f. Fraction: %f\n", integer, fraction)
}
输出:
Integer: 2.000000\. Fraction: 0.500000
Integer: 2.000000\. Fraction: 0.000000
Integer: -2.000000\. Fraction: -0.500000
Integer: 0.000000\. Fraction: 0.500000
Go (Golang)中的 Select 中的 Break 语句
目录
-
概述**
-
代码
概述
break
关键字可以在 select 中使用,以终止执行最内层的语句,类似于 switch 和 for 循环。以下是 select 中break关键字的示例。
代码
package main
import "fmt"
func main() {
ch := make(chan string, 1)
ch <- "Before break"
select {
case msg := <-ch:
fmt.Println(msg)
break
fmt.Println("After break")
default:
fmt.Println("Default case")
}
}
输出
Before break
break语句将终止最内层语句的执行,以下行将永远不会被执行。
fmt.Println("After break")
Go(Golang)中的桥接设计模式
注意:有兴趣了解其他设计模式如何在 GO 中实现吗?请查看这个完整参考 – Go 中的所有设计模式
目录
介绍:
-
UML 图:
-
映射
-
实际例子
-
完整工作代码:## 介绍:
桥接设计模式是一种结构性设计模式,允许将抽象与其实现分离。听起来很困惑?别担心,随着我们深入,它会更清晰。
这个模式建议将一个大类分成两个独立的层次结构。
-
抽象 – 它是一个接口,抽象的子类被称为精炼抽象。抽象包含对实现的引用。
-
实现 – 它也是一个接口,实现的子类被称为具体实现。
抽象层次结构被客户端引用,而不必担心实现。我们来举个例子。假设你有两种类型的计算机mac和windows。再假设有两种类型的打印机epson和hp。计算机和打印机需要以任意组合相互配合。客户端只会访问计算机,而不必担心打印是如何发生的。与其为 2*2 组合创建四个结构,不如创建两个层次结构。
-
抽象层次结构
-
实现层次结构
请查看下面的图。这两个层次通过一个桥接进行通信,其中抽象(这里是计算机)包含对实现(这里是打印机)的引用。抽象和实现可以独立发展,而不相互影响。注意win和mac如何嵌入对printer的引用。我们可以在运行时更改抽象的实现(即计算机的打印机),因为抽象通过接口引用实现。在调用mac.print()或 windows.print()时,它会将请求分派给printer.printFile()。这充当一个桥梁,并为两者提供了松耦合。
UML 图:
映射
下表表示从 UML 图中的参与者到下面“实际例子”中实际实现参与者的映射。
抽象 | computer.go |
---|---|
精炼抽象 1 | win.go |
精炼抽象 2 | mac.go |
实现 | printer.go |
具体实现 1 | epson.go |
具体实现 2 | hp.go |
客户端 | main.go |
实际示例
computer.go
package main
type computer interface {
print()
setPrinter(printer)
}
mac.go
package main
import "fmt"
type mac struct {
printer printer
}
func (m *mac) print() {
fmt.Println("Print request for mac")
m.printer.printFile()
}
func (m *mac) setPrinter(p printer) {
m.printer = p
}
windows.go
package main
import "fmt"
type windows struct {
printer printer
}
func (w *windows) print() {
fmt.Println("Print request for windows")
w.printer.printFile()
}
func (w *windows) setPrinter(p printer) {
w.printer = p
}
printer.go
package main
type printer interface {
printFile()
}
epson.go
package main
import "fmt"
type epson struct {
}
func (p *epson) printFile() {
fmt.Println("Printing by a EPSON Printer")
}
hp.go
package main
import "fmt"
type hp struct {
}
func (p *hp) printFile() {
fmt.Println("Printing by a HP Printer")
}
main.go
package main
import "fmt"
func main() {
hpPrinter := &hp{}
epsonPrinter := &epson{}
macComputer := &mac{}
macComputer.setPrinter(hpPrinter)
macComputer.print()
fmt.Println()
macComputer.setPrinter(epsonPrinter)
macComputer.print()
fmt.Println()
winComputer := &windows{}
winComputer.setPrinter(hpPrinter)
winComputer.print()
fmt.Println()
winComputer.setPrinter(epsonPrinter)
winComputer.print()
fmt.Println()
}
输出:
Print request for mac
Printing by a HP Printer
Print request for mac
Printing by a EPSON Printer
Print request for windows
Printing by a HP Printer
Print request for windows
完整工作代码:
package main
import "fmt"
type computer interface {
print()
setPrinter(printer)
}
type mac struct {
printer printer
}
func (m *mac) print() {
fmt.Println("Print request for mac")
m.printer.printFile()
}
func (m *mac) setPrinter(p printer) {
m.printer = p
}
type windows struct {
printer printer
}
func (w *windows) print() {
fmt.Println("Print request for windows")
w.printer.printFile()
}
func (w *windows) setPrinter(p printer) {
w.printer = p
}
type printer interface {
printFile()
}
type epson struct {
}
func (p *epson) printFile() {
fmt.Println("Printing by a EPSON Printer")
}
type hp struct {
}
func (p *hp) printFile() {
fmt.Println("Printing by a HP Printer")
}
func main() {
hpPrinter := &hp{}
epsonPrinter := &epson{}
macComputer := &mac{}
macComputer.setPrinter(hpPrinter)
macComputer.print()
fmt.Println()
macComputer.setPrinter(epsonPrinter)
macComputer.print()
fmt.Println()
winComputer := &windows{}
winComputer.setPrinter(hpPrinter)
winComputer.print()
fmt.Println()
winComputer.setPrinter(epsonPrinter)
winComputer.print()
fmt.Println()
}
输出:
Print request for mac
Printing by a HP Printer
Print request for mac
Printing by a EPSON Printer
Print request for windows
Printing by a HP Printer
Print request for windows
Go 语言中的冒泡排序
目录
介绍
-
时间复杂度
-
空间复杂度
-
实现:
介绍
在冒泡排序中:
-
在每次迭代 i(从 0 开始)中,我们从第一个元素开始,如果相邻元素顺序错误,就不断交换,直到长度为 (len-i),其中 len 是数组的长度。
-
在迭代结束时,最大的或最小的元素(取决于顺序是升序还是降序)位于位置 (len-i)
时间复杂度
- O(n*n)
空间复杂度
- 冒泡排序的空间复杂度为 O(1)
实现:
package main
import "fmt"
func main() {
sample := []int{3, 4, 5, 2, 1}
bubbleSort(sample)
sample = []int{3, 4, 5, 2, 1, 7, 8, -1, -3}
bubbleSort(sample)
}
func bubbleSort(arr []int) {
len := len(arr)
for i := 0; i < len-1; i++ {
for j := 0; j < len-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
fmt.Println("\nAfter Bubble Sorting")
for _, val := range arr {
fmt.Println(val)
}
}
输出:
After Bubble Sorting
1
2
3
4
5
After Bubble Sorting
-3
-1
1
2
3
4
5
7
8
- 算法* Golang 中的冒泡排序* Golang* 排序*
GoLang 中的建造者模式
目录
** 定义:
-
UML 图
-
映射(另请参见第 5 点 - 示例)
-
何时使用
-
示例:
定义:
建造者模式是一种用于构建复杂对象的创建型设计模式。下面是 UML 图。
注意:想了解其他所有设计模式如何在 GO 中实现。请参见此完整参考 - Go 中的所有设计模式(Golang)
UML 图
映射(另请参见第 5 点 - 示例)
主管 | director.go |
---|---|
建造者接口 | iBuilder.go |
具体建造者 1 | normalBuilder.go |
具体建造者 2 | iglooBuilder.go |
产品 | house.go |
何时使用
-
当构造的对象较大且需要多个步骤时,使用建造者模式。这有助于减少构造函数的大小。房屋的构建变得简单,并且不需要大型构造函数。
-
当需要创建同一产品的不同版本时。例如,在下面的代码中,我们看到由iglooBuilder和normalBuilder构建的不同版本的房屋,即冰屋和普通房屋。
-
当半成品的最终对象不应存在时。再次引用下面的代码,创建的房屋要么完全创建,要么根本不创建。具体建造者结构体保持正在创建的房屋对象的临时状态。
示例:
iBuilder.go
package main
type iBuilder interface {
setWindowType()
setDoorType()
setNumFloor()
getHouse() house
}
func getBuilder(builderType string) iBuilder {
if builderType == "normal" {
return &normalBuilder{}
}
if builderType == "igloo" {
return &iglooBuilder{}
}
return nil
}
normalBuilder.go
package main
type normalBuilder struct {
windowType string
doorType string
floor int
}
func newNormalBuilder() *normalBuilder {
return &normalBuilder{}
}
func (b *normalBuilder) setWindowType() {
b.windowType = "Wooden Window"
}
func (b *normalBuilder) setDoorType() {
b.doorType = "Wooden Door"
}
func (b *normalBuilder) setNumFloor() {
b.floor = 2
}
func (b *normalBuilder) getHouse() house {
return house{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}
iglooBuilder.go
package main
type iglooBuilder struct {
windowType string
doorType string
floor int
}
func newIglooBuilder() *iglooBuilder {
return &iglooBuilder{}
}
func (b *iglooBuilder) setWindowType() {
b.windowType = "Snow Window"
}
func (b *iglooBuilder) setDoorType() {
b.doorType = "Snow Door"
}
func (b *iglooBuilder) setNumFloor() {
b.floor = 1
}
func (b *iglooBuilder) getHouse() house {
return house{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}
house.go
package main
type house struct {
windowType string
doorType string
floor int
}
director.go
package main
type director struct {
builder iBuilder
}
func newDirector(b iBuilder) *director {
return &director{
builder: b,
}
}
func (d *director) setBuilder(b iBuilder) {
d.builder = b
}
func (d *director) buildHouse() house {
d.builder.setDoorType()
d.builder.setWindowType()
d.builder.setNumFloor()
return d.builder.getHouse()
}
main.go
package main
import "fmt"
func main() {
normalBuilder := getBuilder("normal")
iglooBuilder := getBuilder("igloo")
director := newDirector(normalBuilder)
normalHouse := director.buildHouse()
fmt.Printf("Normal House Door Type: %s\n", normalHouse.doorType)
fmt.Printf("Normal House Window Type: %s\n", normalHouse.windowType)
fmt.Printf("Normal House Num Floor: %d\n", normalHouse.floor)
director.setBuilder(iglooBuilder)
iglooHouse := director.buildHouse()
fmt.Printf("\nIgloo House Door Type: %s\n", iglooHouse.doorType)
fmt.Printf("Igloo House Window Type: %s\n", iglooHouse.windowType)
fmt.Printf("Igloo House Num Floor: %d\n", iglooHouse.floor)
}
输出:
Normal House Door Type: Wooden Door
Normal House Window Type: Wooden Window
Normal House Num Floor: 2
Igloo House Door Type: Snow Door
Igloo House Window Type: Snow Window
Igloo House Num Floor: 1
计算 x^y – Go 中的 Pow()
目录
-
概述
-
代码
概述
GO 的math包提供了一个Pow方法,可以用来计算 x 的 y 次幂。
以下是该函数的签名。它接受两个浮点参数作为输入,并返回一个浮点数。
func Pow(x, y float64) float64
同样的函数也可以用来计算一个数字的平方或立方。只需在平方情况下将第二个参数 y 传入 2,在立方情况下传入 3 即可。
代码
package main
import (
"fmt"
"math"
)
func main() {
//Power for integers
res := math.Pow(2, 10)
fmt.Println(res)
//Power for float
res = math.Pow(1.5, 2)
fmt.Println(res)
//Anything to power 0 is 1
res = math.Pow(3, 0)
fmt.Println(res)
}
输出:
1024
2.25
1
在 Go (Golang) 中常量可以在声明后重新赋值吗
目录
-
概述
-
示例
概述
常量变量在声明后无法重新赋值,因为它是常量,且其值无法更改。如果尝试给常量变量重新赋值,则会引发编译错误。
示例
例如,下面的代码将引发编译错误
package main
func main() {
const a int = 8
a = 9
}
输出
main.go:5:4: cannot assign to a
在上述程序中,我们首先创建了一个常量
const a int = 8
然后我们尝试将新值 9 赋给常量a,因此会引发编译错误,因为常量一旦声明后无法重新赋值。
在 Go (Golang) 中,main
函数内部可以使用 defer 吗?
目录
-
概述
-
示例
概述
正如名称所示,defer 用于延迟函数中的清理活动。这些清理活动将在函数结束时执行。
defer
也可以在 main
函数内部使用的原因。让我们看一个例子。
示例
package main
import "fmt"
func main() {
defer test()
fmt.Println("Executed in main")
}
func test() {
fmt.Println("In Defer")
}
输出
Executed in main
In Defer
在上述程序中,有一个 defer 语句调用名为 test 的自定义函数。从输出可以看出,test 函数在 main
中的所有内容执行完毕后被调用,并在 main
返回之前调用。这就是
Executed in main
在输出之前打印。
In Defer
上述函数同样表明在 main
函数中使用 defer 是完全可以的。
规范的 HTTP 头部键含义
规范形式意味着第一个字符和其后任何跟随的连字符后的字符均为大写。其他所有字符均为小写。规范形式的示例有
Content-Type
Accept-Encoding
所有头部中的键都仅以规范形式表示。例如,如果在传入的 HTTP 请求中有以下头部
content-type: applcation/json
accept-encoding: gzip
然后在服务器端,不同语言的 HTTP 库大多会将其解释为规范形式。例如,当我们在服务器端打印头部时,上述头部将如下所示。
Content-Type: application/json
Accept-Encoding: gzip
注意
-
content-type 被转换为规范形式 Content-Type
-
accept-encoding 被转换为规范形式 Accept-Encoding
在 Go(Golang)中将字符串首字母大写
目录
-
概述
-
代码:
概述
在 Golang 中,字符串是 UTF-8 编码的。Go 的strings包提供了一个Title方法,可以将句子中所有单词的首字母转换为大写。它返回一个字符串的副本,所有单词的首字母都被大写。
下面是该函数的签名
func Title(s string) string
让我们来看一下工作代码
代码:
package main
import (
"fmt"
"strings"
)
func main() {
res := strings.Title("this is a test sentence")
fmt.Println(res)
}
输出:
This Is A Test Sentence
```*
<!--yml
分类:未分类
日期:2024-10-13 06:14:08
-->
# Go(Golang)中的不区分大小写字符串比较
> 来源:[`golangbyexample.com/golang-case-insensitive-string-comparison/`](https://golangbyexample.com/golang-case-insensitive-string-comparison/)
目录
+ 概述
+ 代码:
# **概述**
在 Golang 中,字符串采用 UTF-8 编码。GO 的**strings**包提供了一个**EqualFold**方法,可以用来进行两个字符串的不区分大小写的比较。
以下是该函数的签名。该方法返回布尔值,指示提供的两个字符串是否不区分大小写地相等。
```go
func EqualFold(s, t string) bool
代码:
package main
import (
"fmt"
"strings"
)
func main() {
res := strings.EqualFold("abc", "ABC")
fmt.Println(res)
res = strings.EqualFold("abc", "aBC")
fmt.Println(res)
res = strings.EqualFold("abc", "AbC")
fmt.Println(res)
res = strings.EqualFold("abc", "AbCd")
fmt.Println(res)
}
输出:
true
true
true
false
在 Go 中的 cd 命令或更改当前工作目录
概述
os.Chdir() 用于将当前工作目录更改为在 Golang 中指定的目录。它类似于 cd 命令。
以下是该函数的签名
func Chdir(dir string) error
代码
package main
import (
"fmt"
"os"
)
func main() {
os.Chdir("/Users")
newDir, err := os.Getwd()
if err != nil {
}
fmt.Printf("Current Working Direcotry: %s\n", newDir)
}
输出:
Current Working Direcoty: /Users
Go (Golang)中的向上取整
目录
-
概述
-
代码:
概述
math包提供了一个Ceil方法,可以用于获取一个数字的向上取整值。一个数字的向上取整是大于或等于该数字的最小整数值。
以下是该函数的签名。它接受一个浮点数作为输入,并返回一个浮点数。
func Ceil(x float64) float64
向上取整函数的一些特殊情况是
-
向上取整(±0) = ±0
-
向上取整(±Inf) = ±Inf
-
向上取整(NaN) = NaN
代码:
package main
import (
"fmt"
"math"
)
func main() {
res := math.Ceil(1.6)
fmt.Println(res)
res = math.Ceil(-1.6)
fmt.Println(res)
res = math.Ceil(1)
fmt.Println(res)
}
输出:
2
-1
1
责任链设计模式在 Go (Golang) 中的应用
来源:
golangbyexample.com/chain-of-responsibility-design-pattern-in-golang/
注意:如果您想了解所有其他设计模式如何在 GO 中实现,请查看这个完整参考 – Go (Golang) 中的所有设计模式
目录
** 定义:
-
使用时机:
-
UML 图:
-
映射
-
实际例子:
-
完整工作代码:
**定义: **
责任链设计模式是一种行为设计模式。它允许您创建一条请求处理者链。对于每一个传入请求,它会在链中传递,每个处理者:
-
处理请求或跳过处理。
-
决定是否将请求传递给链中的下一个处理者。
责任链设计模式最好通过示例来理解。让我们以医院为例。医院有多个部门,例如:
-
接待
-
医生
-
药品室
-
收银员
每当有患者到达时,他首先去接待处,然后去医生,接着是药品室,然后是收银员,依此类推。患者以某种方式被送入一个部门链,在完成后,将患者发送到进一步的部门。这就是责任链模式发挥作用的地方。
使用时机:
-
当有多个候选者处理相同请求时,该模式适用。
-
当您不希望客户端选择接收者时,因为多个对象可以处理请求。此外,您希望将客户端与接收者解耦。客户端只需知道链中的第一个元素。
在医院的例子中,患者首先去接待处,然后接待处根据患者的当前状态将其发送到链中的下一个处理者。
UML 图:
以下是与实际例子相对应的映射 UML 图。
**映射 **
处理者 | department.go |
---|---|
具体处理者 1 | account.go |
具体处理者 2 | doctor.go |
具体处理者 3 | medical.go |
具体处理者 4 | cashier.go |
客户端 | main.go |
实际例子:
department.go
package main
type department interface {
execute(*patient)
setNext(department)
}
reception.go
package main
import "fmt"
type reception struct {
next department
}
func (r *reception) execute(p *patient) {
if p.registrationDone {
fmt.Println("Patient registration already done")
r.next.execute(p)
return
}
fmt.Println("Reception registering patient")
p.registrationDone = true
r.next.execute(p)
}
func (r *reception) setNext(next department) {
r.next = next
}
doctor.go
package main
import "fmt"
type doctor struct {
next department
}
func (d *doctor) execute(p *patient) {
if p.doctorCheckUpDone {
fmt.Println("Doctor checkup already done")
d.next.execute(p)
return
}
fmt.Println("Doctor checking patient")
p.doctorCheckUpDone = true
d.next.execute(p)
}
func (d *doctor) setNext(next department) {
d.next = next
}
medical.go
package main
import "fmt"
type medical struct {
next department
}
func (m *medical) execute(p *patient) {
if p.medicineDone {
fmt.Println("Medicine already given to patient")
m.next.execute(p)
return
}
fmt.Println("Medical giving medicine to patient")
p.medicineDone = true
m.next.execute(p)
}
func (m *medical) setNext(next department) {
m.next = next
}
cashier.go
package main
import "fmt"
type cashier struct {
next department
}
func (c *cashier) execute(p *patient) {
if p.paymentDone {
fmt.Println("Payment Done")
}
fmt.Println("Cashier getting money from patient patient")
}
func (c *cashier) setNext(next department) {
c.next = next
}
patient.go
package main
type patient struct {
name string
registrationDone bool
doctorCheckUpDone bool
medicineDone bool
paymentDone bool
}
main.go
package main
func main() {
cashier := &cashier{}
//Set next for medical department
medical := &medical{}
medical.setNext(cashier)
//Set next for doctor department
doctor := &doctor{}
doctor.setNext(medical)
//Set next for reception department
reception := &reception{}
reception.setNext(doctor)
patient := &patient{name: "abc"}
//Patient visiting
reception.execute(patient)
}
输出:
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient
完整工作代码:
package main
import "fmt"
type department interface {
execute(*patient)
setNext(department)
}
type reception struct {
next department
}
func (r *reception) execute(p *patient) {
if p.registrationDone {
fmt.Println("Patient registration already done")
r.next.execute(p)
return
}
fmt.Println("Reception registering patient")
p.registrationDone = true
r.next.execute(p)
}
func (r *reception) setNext(next department) {
r.next = next
}
type doctor struct {
next department
}
func (d *doctor) execute(p *patient) {
if p.doctorCheckUpDone {
fmt.Println("Doctor checkup already done")
d.next.execute(p)
return
}
fmt.Println("Doctor checking patient")
p.doctorCheckUpDone = true
d.next.execute(p)
}
func (d *doctor) setNext(next department) {
d.next = next
}
type medical struct {
next department
}
func (m *medical) execute(p *patient) {
if p.medicineDone {
fmt.Println("Medicine already given to patient")
m.next.execute(p)
return
}
fmt.Println("Medical giving medicine to patient")
p.medicineDone = true
m.next.execute(p)
}
func (m *medical) setNext(next department) {
m.next = next
}
type cashier struct {
next department
}
func (c *cashier) execute(p *patient) {
if p.paymentDone {
fmt.Println("Payment Done")
}
fmt.Println("Cashier getting money from patient patient")
}
func (c *cashier) setNext(next department) {
c.next = next
}
type patient struct {
name string
registrationDone bool
doctorCheckUpDone bool
medicineDone bool
paymentDone bool
}
func main() {
cashier := &cashier{}
//Set next for medical department
medical := &medical{}
medical.setNext(cashier)
//Set next for doctor department
doctor := &doctor{}
doctor.setNext(medical)
//Set next for reception department
reception := &reception{}
reception.setNext(doctor)
patient := &patient{name: "abc"}
//Patient visiting
reception.execute(patient)
}
输出:
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient
在 Go (Golang) 中更改文件权限
os.Chmod() 函数可用于更改现有文件的权限。下面是该函数的签名
func Chmod(name string, mode FileMode) error
代码
package main
import (
"fmt"
"log"
"os"
)
func main() {
// Create new file
new, err := os.Create("new.txt")
if err != nil {
log.Fatal(err)
}
defer new.Close()
stats, err := os.Stat("new.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Permission File Before: %s\n", stats.Mode())
err = os.Chmod("new.txt", 0700)
if err != nil {
log.Fatal(err)
}
stats, err = os.Stat("new.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Permission File After: %s\n", stats.Mode())
}
输出:
Permission File Before: -rw-r--r--
Permission File After: -rwx------
在 Go (Golang) 中更改文件夹/目录权限
来源:
golangbyexample.com/change-folder-directory-permissions-go/
os.Chmod() 函数可用于更改现有文件夹或目录的权限。以下是该函数的签名。
func Chmod(name string, mode FileMode) error
代码
package main
import (
"fmt"
"log"
"os"
)
func main() {
err := os.Mkdir("new", 0755)
if err != nil {
log.Fatal(err)
}
stats, err := os.Stat("new")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Permission Folder Before: %s\n", stats.Mode())
err = os.Chmod("new", 0700)
if err != nil {
log.Fatal(err)
}
stats, err = os.Stat("new")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Permission Folder After: %s\n", stats.Mode())
}
输出:
Permission Folder Before: drwxr-xr-x
Permission Folder After: drwx------
在 Go(Golang)中更改文件的修改/更新时间和访问时间
os.Chtimes() 函数可以用来更改 Golang 中一个文件的 mtime(修改时间)或 atime(访问时间)。下面是该函数的签名。
func Chtimes(name string, atime time.Time, mtime time.Time)
代码:
package main
import (
"fmt"
"os"
"time"
)
func main() {
fileName := "sample.txt"
currentTime := time.Now().Local()
//Set both access time and modified time of the file to the current time
err := os.Chtimes(fileName, currentTime, currentTime)
if err != nil {
fmt.Println(err)
}
}
输出:
Changes the atime and mtime of the file.
在 Go 语言中将通道作为函数参数
目录
-
概述
-
双向通道
-
仅发送通道
-
仅接收通道
-
通道指针
概述
通道可以作为函数参数传递的方式有很多。箭头的方向指定数据流的方向
-
chan : 双向通道(可读可写)
-
chan <- : 仅写入通道
-
<- chan : 仅从通道读取(输入通道)
-
*chan : 通道指针。可读可写
双向通道
这样的双向通道的签名在作为函数参数传递时如下所示。
func process(ch chan int){ //doSomething }
代码:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
process(ch)
}
func process(ch chan int) {
ch <- 2
s := <-ch
fmt.Println(s)
}
输出: 2
仅发送通道
- 只能发送数据的通道的签名在作为函数参数传递时如下所示。
func process(ch chan<- int){ //doSomething }
- 尝试从这样的通道接收数据时会出现以下错误。
invalid operation: <-ch (receive from send-only type chan<- int)
尝试取消注释代码中的以下行以查看上述错误
s := <-ch
代码:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
process(ch)
fmt.Println(<-ch)
}
func process(ch chan<- int) {
ch <- 2
//s := <-ch
}
输出: 2
仅接收通道
- 仅接收数据的通道的签名在作为函数参数传递时如下所示
func process(ch <-chan int){ //doSomething }
- 尝试向这样的通道发送数据时会出现以下错误。
invalid operation: ch <- 2 (send to receive-only type <-chan int)
尝试取消注释代码中的以下行以查看上述错误
ch <- 2
代码:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 2
process(ch)
fmt.Println()
}
func process(ch <-chan int) {
s := <-ch
fmt.Println(s)
//ch <- 2
}
输出: 2
通道指针
以这种方式传递通道只有在您想要修改通道时才有意义。这是非常不常见的,且不是一种可取的使用方式。这样的通道作为指针传递的签名。
func process(ch *chan int){ //doSomething}
代码:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
process(&ch)
fmt.Println(ch)
}
func process(ch *chan int) {
*ch <- 2
s := <-*ch
*ch = nil
fmt.Println(s)
}
输出: 2*
Go(Golang)中的通道方向
目录
-
概述
-
仅发送到通道
-
仅接收来自通道
概述
在 golang 中可以创建双向和单向通道。可以创建一个只能发送数据的通道,也可以创建一个只能接收数据的通道。这由通道的箭头方向决定。通道的箭头方向指定数据流动的方向。
-
chan :双向通道(同时读写)
-
chan <- :仅向通道写入
-
<- chan :仅从通道读取(输入通道)
一个只能发送数据的通道。
这是这样的通道的语法。
chan<- int
一个只能发送数据的通道
这是这样的通道的语法。
<-chan int
现在的问题是,为什么你想创建一个只能发送数据的通道或只能接收数据的通道。当我们希望将通道传递给一个函数,并限制该函数只能发送数据或接收数据时,这就很方便。
有很多方法可以将通道作为函数参数传递。
-
chan :双向通道(同时读写)
-
chan <- :仅向通道写入
-
<- chan :仅从通道读取(输入通道)
仅发送到通道
- 这样的通道只能发送数据,当作为参数传递给函数时,将如下所示。
func process(ch chan<- int){ //doSomething }
- 尝试从这样的通道接收数据将会产生以下错误。
invalid operation: <-ch (receive from send-only type chan<- int)
尝试取消注释下面的代码行以查看上述错误。
s := <-ch
代码:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
process(ch)
fmt.Println(<-ch)
}
func process(ch chan<- int) {
ch <- 2
//s := <-ch
}
输出: 2
仅接收来自通道
- 这样的通道的签名,当作为参数传递给函数时,将如下所示。
func process(ch <-chan int){ //doSomething }
- 尝试向这样的通道发送数据将会产生以下错误。
invalid operation: ch <- 2 (send to receive-only type <-chan int)
尝试取消注释下面的代码行以查看上述错误。
ch <- 2
代码:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 2
process(ch)
fmt.Println()
}
func process(ch <-chan int) {
s := <-ch
fmt.Println(s)
//ch <- 2
}
输出: 2