通过示例学习-Go-语言-2023-六-

通过示例学习 Go 语言 2023(六)

在 Go (Golang)中检查字符串是否以前缀开头

来源:golangbyexample.com/string-begins-prefix-go/

目录

  • 概述

  • 代码:

概述

在 GO 中,字符串是 UTF-8 编码的。GO 的strings包提供了一个HasPrefix方法,可以用来检查字符串是否以某个前缀开头

以下是函数的签名

func HasPrefix(s, prefix string) bool

让我们看看工作代码

代码:

package main

import (
    "fmt"
    "strings"
)

func main() {
    //Input string contains the prefix
    res := strings.HasPrefix("abcdef", "ab")
    fmt.Println(res)

    //Input string doesn't contain the prefix
    res = strings.HasPrefix("abcdef", "ac")
    fmt.Println(res)
}

输出:

true
false
```*


<!--yml

类别:未分类

日期:2024-10-13 06:46:23

-->

# 检查两个给定字符串在 Go (Golang) 中是否为变位词

> 来源:[`golangbyexample.com/check-two-strings-anagram-go/`](https://golangbyexample.com/check-two-strings-anagram-go/)

目录

+   概述

+   程序

## **概述**

**变位词** 是通过重新排列另一个单词或短语的字母形成的单词或短语,通常使用所有原始字母且恰好一次。例如,单词 *anagram* 本身可以重新排列为 *nagaram*,而单词 *binary* 可以变为 *brainy*,单词 *adobe* 可以变为 *abode*。

例如

```go
Input: abc, bac
Ouput: true

Input: abc, cb
Ouput: false

这里是实现方法的思路。创建一个字符串到整数的映射。现在

  • 遍历第一个字符串并增加映射中每个字符的计数

  • 遍历第二个字符串并减少映射中每个字符的计数

  • 再次遍历第一个字符串,如果在映射中某个字符的计数非零,则返回 false。

  • 最后返回 true

程序

这里是相应的程序。

package main

import "fmt"

func isAnagram(s string, t string) bool {

	lenS := len(s)
	lenT := len(t)

	if lenS != lenT {
		return false
	}

	anagramMap := make(map[string]int)

	for i := 0; i < lenS; i++ {
		anagramMap[string(s[i])]++
	}

	for i := 0; i < lenT; i++ {
		anagramMap[string(t[i])]--
	}

	for i := 0; i < lenS; i++ {
		if anagramMap[string(s[i])] != 0 {
			return false
		}
	}

	return true
}

func main() {
	output := isAnagram("abc", "bac")
	fmt.Println(output)

	output = isAnagram("abc", "bc")
	fmt.Println(output)
}

输出

true
false

注意: 查看我们的 Golang 高级教程。本系列的教程内容详尽,努力涵盖所有概念及示例。本教程适合那些希望获得专业知识并深入理解 golang 的人 - Golang 高级教程

如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章就是为你准备的 - 所有设计模式 Golang

检查两个结构体是否相等或在 Go (Golang) 中的结构体相等性。

来源:golangbyexample.com/struct-equality-golang/

在考虑结构体相等性之前,首先要知道所有结构体字段的类型是否可比较。

根据 Go 规范定义的一些可比较类型包括:

  • 布尔值

  • 数值

  • 字符串,

  • 指针

  • 渠道

  • 接口类型

  • 结构体 – 如果它的所有字段类型都是可比较的。

  • 数组 – 如果数组元素的值类型是可比较的。

根据 Go 规范,不可比较的类型以及不能用作映射键的类型包括:

  • 切片

  • 映射

  • 函数

如果两个结构体的所有字段类型可比较且所有对应字段值相等,则它们是相等的。

让我们看一个例子。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp1 := employee{name: "Sam", age: 31, salary: 2000}
    emp2 := employee{name: "Sam", age: 31, salary: 2000}
    if emp1 == emp2 {
        fmt.Println("emp1 annd emp2 are equal")
    } else {
        fmt.Println("emp1 annd emp2 are not equal")
    }
}

输出

emp1 annd emp2 are equal

如果结构体字段类型不可比较,则在使用 == 运算符检查结构体相等性时会出现编译错误。

package main

import "fmt"

type employee struct {
    name        string
    age         int
    salary      int
    departments []string
}

func main() {
    emp1 := employee{name: "Sam", age: 31, salary: 2000, departments: []string{"CS"}}
    emp2 := employee{name: "Sam", age: 31, salary: 2000, departments: []string{"EC"}}
    if emp1 == emp2 {
        fmt.Println("emp1 annd emp2 are equal")
    } else {
        fmt.Println("emp1 annd emp2 are not equal")
    }
}

上述程序会引发编译错误,因为 employee 结构体包含一个字段 departments,这是一个 slice 类型的 stringslice 不是可比较类型,因此会出现编译错误。

invalid operation: emp1 == emp2 (struct containing []string cannot be compared)

检查 Go (Golang) 中的有效括号

来源:golangbyexample.com/valid-parenthesis-golang/

目录

  • 概述

  • 程序

概述

有一个输入字符串,只包含以下字符。

  • (

  • )

  • {

  • }

  • [

  • ]

目标是检查输入字符串是否具有有效的括号。一个括号是有效的,如果

*** 开放括号应该按照相同的顺序闭合。这意味着下面的内容将是无效的,因为括号没有按照正确的顺序闭合。

([)]

  • 每个开放括号都应有对应的闭合括号。

有效括号示例。

()
{}
[]
()[]
([{}])
()[{}]

无效括号示例。

([)]
(
{
[
(()

程序

思路是使用栈。

  • 如果给定的括号是左括号,我们将其推入栈中。

  • 如果给定的括号是右括号,我们将其从栈中弹出,并检查弹出的左括号是否与当前的右括号对应。例如,如果我们在字符串中遇到‘]’,我们弹出并检查弹出的值是否仅为‘[‘。如果不是,则返回 false。‘)’‘}’也同理。

  • 在字符串的末尾,栈应该是空的。

以下是相同的程序。

package main

import (
	"container/list"
	"fmt"
)

func main() {
	valid := isValid("()")
	fmt.Println(valid)

	valid = isValid("[]")
	fmt.Println(valid)

	valid = isValid("{}")
	fmt.Println(valid)

	valid = isValid("()[]")
	fmt.Println(valid)

	valid = isValid("([{}])")
	fmt.Println(valid)

	valid = isValid("()[{}]")
	fmt.Println(valid)

	valid = isValid("([)]")
	fmt.Println(valid)

	valid = isValid("(")
	fmt.Println(valid)

	valid = isValid("(()")
	fmt.Println(valid)

}

type customStack struct {
	stack *list.List
}

func (c *customStack) Push(value string) {
	c.stack.PushFront(value)
}

func (c *customStack) Pop() error {
	if c.stack.Len() > 0 {
		ele := c.stack.Front()
		c.stack.Remove(ele)
	}
	return fmt.Errorf("Pop Error: Stack is empty")
}

func (c *customStack) Front() (string, error) {
	if c.stack.Len() > 0 {
		if val, ok := c.stack.Front().Value.(string); ok {
			return val, nil
		}
		return "", fmt.Errorf("Peep Error: Stack Datatype is incorrect")
	}
	return "", fmt.Errorf("Peep Error: Stack is empty")
}

func (c *customStack) Size() int {
	return c.stack.Len()
}

func (c *customStack) Empty() bool {
	return c.stack.Len() == 0
}

func isValid(s string) bool {
	customStack := &customStack{
		stack: list.New(),
	}

	for _, val := range s {

		if val == '(' || val == '[' || val == '{' {
			customStack.Push(string(val))
		} else if val == ')' {
			poppedValue, _ := customStack.Front()
			if poppedValue != "(" {
				return false
			}
			customStack.Pop()
		} else if val == ']' {
			poppedValue, _ := customStack.Front()
			if poppedValue != "[" {
				return false
			}
			customStack.Pop()

		} else if val == '}' {
			poppedValue, _ := customStack.Front()
			if poppedValue != "{" {
				return false
			}
			customStack.Pop()

		}

	}

	return customStack.Size() == 0
}

输出

true
true
true
true
true
true
false
false
false

注意: 查看我们的 Golang 高级教程。该系列教程内容详尽,我们试图用示例覆盖所有概念。本教程适合那些希望获得 Golang 专业知识和扎实理解的人 – Golang 高级教程

如果你有兴趣了解所有设计模式如何在 Golang 中实现。如果是的话,那么这篇文章就是为你准备的 – 所有设计模式 Golang

Go (Golang) 中通道的关闭操作。

来源:golangbyexample.com/close-operation-on-a-channel-in-go-golang/

关闭是一个内置函数,可以用于关闭通道。关闭通道意味着不能再向通道发送更多数据。当所有数据都已发送且没有更多数据要发送时,通道通常会被关闭。让我们看看一个程序。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    go sum(ch, 3)
    ch <- 2
    ch <- 2
    ch <- 2
    close(ch)
    time.Sleep(time.Second * 1)
}

func sum(ch chan int, len int) {
    sum := 0
    for i := 0; i < len; i++ {
        sum += <-ch
    }
    fmt.Printf("Sum: %d\n", sum)
}

输出

Sum: 6

在上面的程序中,我们创建了一个通道。然后在一个 goroutine 中调用了求和函数。在主函数中,我们向通道发送了 3 个值,之后关闭了通道,表示不再向通道发送更多值。求和函数使用 for 循环遍历通道并计算和。

在一个关闭的通道上发送会导致恐慌。请查看下面的程序。

package main
func main() {
    ch := make(chan int)
    close(ch)
    ch <- 2
}

输出

panic: send on closed channel

关闭一个已经关闭的通道也会导致恐慌。

在从通道接收时,我们还可以使用一个附加变量来确定通道是否已关闭。下面是相应的语法。

val,ok <- ch

ok 的值将是

  • 如果通道未关闭则为真。

  • 如果通道已关闭则为假。

package main
import (
    "fmt"
)
func main() {
    ch := make(chan int, 1)
    ch <- 2
    val, ok := <-ch
    fmt.Printf("Val: %d OK: %t\n", val, ok)

    close(ch)
    val, ok = <-ch
    fmt.Printf("Val: %d OK: %t\n", val, ok)
}

输出

Val: 2 OK: true
Val: 0 OK: false

在上面的程序中创建了一个容量为一的通道。然后我们向通道发送了一个值。第一个接收中的ok变量为真,因为通道未关闭。第二个接收中的ok变量为假,因为通道已关闭。

通道上的范围循环

范围循环可以用来从通道接收数据,直到它被关闭。

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)
	ch <- 2
	ch <- 2
	ch <- 2
	close(ch)
	sum(ch)
	time.Sleep(time.Second * 1)
}

func sum(ch chan int) {
	sum := 0
	for val := range ch {
		sum += val
	}
	fmt.Printf("Sum: %d\n", sum)
}

输出

Sum: 6

在上面的程序中,我们创建了一个通道。在主函数中,我们向通道发送了三个值,之后关闭了通道。然后调用了求和函数,并将通道传递给该函数。在求和函数中,我们对通道进行了范围循环。遍历完通道中的所有值后,范围循环将退出,因为通道已关闭。

现在浮现的问题是,如果在主函数中不关闭通道会发生什么。尝试注释掉关闭通道的那一行。现在运行程序。它也会输出死锁,因为范围循环在求和函数中将永远不会结束。

fatal error: all goroutines are asleep - deadlock!

让我们看看摘要表,展示不同类型通道关闭操作的结果。

命令 无缓冲通道(未关闭且不为 nil) 缓冲通道(未关闭且不为 nil) 已关闭通道 空通道
关闭 成功 成功 恐慌 恐慌

Go(Golang)中的组合求和程序

来源:golangbyexample.com/combination-sum-program-go/

目录

  • 概述

  • 程序

概述

给定一个整数数组和一个目标数字,目标是找到数组中组合的数量,使得它们的和等于目标数字。

数组中的每个元素可以在同一组合中使用任意次数。

示例

Input: [3,4,10,11]
Target: 10
Output: [[3,3,4],[10]]

程序

这里是相同程序的代码。

package main

import "fmt"

func combinationSum(candidates []int, target int) [][]int {
	lengthCandidates := len(candidates)
	current_sum_array := make([]int, 0)
	output := make([][]int, 0)
	combinationSumUtil(candidates, lengthCandidates, 0, 0, 0, target, current_sum_array, &output)
	return output
}

func combinationSumUtil(candidates []int, lengthCandidates, index, current_sum_index, current_sum, target int, current_sum_array []int, output *[][]int) {

	if index >= lengthCandidates {
		return
	}

	if current_sum > target {
		return
	}

	if current_sum == target {
		var o []int
		for i := 0; i < current_sum_index; i++ {
			o = append(o, current_sum_array[i])
		}
		*output = append(*output, o)
		return
	}

	//Exclude
	combinationSumUtil(candidates, lengthCandidates, index+1, current_sum_index, current_sum, target, current_sum_array, output)

	//Include
	current_sum_array = append(current_sum_array, candidates[index])

	combinationSumUtil(candidates, lengthCandidates, index, current_sum_index+1, current_sum+candidates[index], target, current_sum_array, output)
}

func main() {
	output := combinationSum([]int{3, 4, 10, 11}, 10)
	fmt.Println(output)
}

输出

[[10] [3 3 4]]

注意: 请查看我们的 Golang 高级教程。本系列教程内容详尽,我们努力用示例覆盖所有概念。这个教程适合那些希望获得专业知识和扎实理解 golang 的人 - Golang 高级教程

如果你有兴趣了解所有设计模式如何在 Golang 中实现。如果是的话,这篇文章适合你 - 所有设计模式 Golang

Go(Golang)中的命令设计模式

来源:golangbyexample.com/command-design-pattern-in-golang/

注意:如果你有兴趣了解如何在 GO 中实现所有其他设计模式,请查看这个完整的参考 – golangbyexample.com/all-design-patterns-golang/

目录

**介绍:

  • UML 图:

  • 映射

  • 实际示例:

  • 完整工作代码:**## 介绍:

命令设计模式是一种行为设计模式。它建议将请求封装为一个独立的对象。创建的对象拥有关于请求的所有信息,因此可以独立执行。

命令设计模式中使用的基本组件有:

  • 接收者 – 它是包含业务逻辑的类。命令对象仅将请求延迟到接收者。

  • 命令 – 嵌入接收者并绑定接收者的特定操作。

  • 调用者 – 它将命令嵌入并通过调用命令的执行方法来激活该命令。

  • 客户端 – 它创建命令,并通过将接收者传递给命令的构造函数来关联适当的接收者。之后,它还将结果命令与调用者关联起来。

让我们理解一个情况,之后会清楚为什么命令模式是有用的。想象一下电视的情况。电视可以通过以下方式打开ON

  1. 遥控器的开启按钮

  2. 电视上的开启按钮。

这两个触发点的功能相同,即打开电视。为了打开电视,我们可以将接收者实现为电视的开启命令对象。当对这个开启命令对象调用 execute()方法时,它会调用 TV.on()函数。所以在这种情况下:

  • 接收者电视

  • 命令是嵌入电视开启命令对象

  • 调用者遥控器的开启按钮电视上的开启按钮。两者都嵌入开启命令对象

请注意,我们将打开电视的请求封装成一个开启命令对象,可以由多个调用者调用。这个开启命令对象嵌入了接收者(这里是电视),并可以独立执行。

另一个例子,想象一下Adobe Photoshop应用程序的情况。在 Photoshop 中,可以从 3 个地方触发保存操作。

  1. 从菜单中。

  2. 从上方工具栏上的按钮。

  3. 使用快捷键 Ctrl+S。

这三个触发点的功能相同,即保存当前应用中的图像。这个保存可以封装成一个带有当前打开图像的保存命令对象。

在上述示例中创建一个单独的命令对象有什么好处?

  1. 它将 UI 逻辑与底层业务逻辑解耦。

  2. 不需要为每个调用者创建不同的处理程序。

  3. 命令对象包含执行所需的所有信息。因此,它也可以用于延迟执行。

现在让我们来看一下 UML 图。

UML 图:

映射

下表展示了 UML 图中参与者与 “实际示例” 中实际实现参与者之间的映射。

调用者 button.go
命令接口 command.go
具体命令 1 onCommand.go
具体命令 2 offCommand.go
接收者接口 device.go
具体接收者 tv.go
客户端 main.go

实际示例:

button.go

package main

type button struct {
    command command
}

func (b *button) press() {
    b.command.execute()
}

command.go

package main

type command interface {
    execute()
}

onCommand.go

package main

type onCommand struct {
    device device
}

func (c *onCommand) execute() {
    c.device.on()
}

offCommand.go

package main

type offCommand struct {
    device device
}

func (c *offCommand) execute() {
    c.device.off()
}

device.go

package main

type device interface {
    on()
    off()
}

tv.go

package main

import "fmt"

type tv struct {
    isRunning bool
}

func (t *tv) on() {
    t.isRunning = true
    fmt.Println("Turning tv on")
}

func (t *tv) off() {
    t.isRunning = false
    fmt.Println("Turning tv off")
}

main.go

package main

func main() {
    tv := &tv{}
    onCommand := &onCommand{
        device: tv,
    }
    offCommand := &offCommand{
        device: tv,
    }
    onButton := &button{
        command: onCommand,
    }
    onButton.press()
    offButton := &button{
        command: offCommand,
    }
    offButton.press()
}

输出:

Turning tv on
Turning tv off

完整工作代码:

package main

import "fmt"

type button struct {
    command command
}

func (b *button) press() {
    b.command.execute()
}

type command interface {
    execute()
}

type offCommand struct {
    device device
}

func (c *offCommand) execute() {
    c.device.off()
}

type onCommand struct {
    device device
}

func (c *onCommand) execute() {
    c.device.on()
}

type device interface {
    on()
    off()
}

type tv struct {
    isRunning bool
}

func (t *tv) on() {
    t.isRunning = true
    fmt.Println("Turning tv on")
}

func (t *tv) off() {
    t.isRunning = false
    fmt.Println("Turning tv off")
}

func main() {
    tv := &tv{}
    onCommand := &onCommand{
        device: tv,
    }
    offCommand := &offCommand{
        device: tv,
    }
    onButton := &button{
        command: onCommand,
    }
    onButton.press()
    offButton := &button{
        command: offCommand,
    }
    offButton.press()
}

输出:

Turning tv on
Turning tv off

在 Go (Golang)中比较错误或错误相等

来源:golangbyexample.com/comparing-error-go/

目录

  • 概述

  • 代码

概述

首先,什么是错误的相等性?正如您所知道的,错误在 Go 中由错误接口表示。在 Go 中,如果两个接口是相等的

  • 两者指向相同的底层类型

  • 底层值相等(或两个都是 nil)

所以上述两点也适用于比较错误。有两种方法可以检查给定的错误是否相等。

  • 使用相等运算符 (==)

  • 使用错误包的 Is 函数 – golang.org/pkg/errors/。 使用 Is 函数优于使用相等运算符,因为它通过逐步解包第一个错误来检查相等性,并在每一步解包时与目标错误匹配。稍后我们将看到一个例子,以充分理解为什么它更可取。下面是 Is 函数的语法。

func Is(err, target error) bool

代码

让我们看一个例子

package main
import (
    "errors"
    "fmt"
)
type errorOne struct{}
func (e errorOne) Error() string {
    return "Error One happended"
}
func main() {
    var err1 errorOne
    err2 := do()
    if err1 == err2 {
        fmt.Println("Equality Operator: Both errors are equal")
    }
    if errors.Is(err1, err2) {
        fmt.Println("Is function: Both errors are equal")
    }
}
func do() error {
    return errorOne{}
}

输出

Equality Operator: Both errors are equal
Is function: Both errors are equal

在上面的程序中,我们创建了errorOne结构,它定义了Error方法,从而实现了error接口。我们创建了err1变量,它是errorOne结构的一个实例。我们还创建了一个do()函数,该函数引发errorOne类型的错误,并在主函数中捕获到err2变量中。

然后我们使用

  • 使用相等运算符
err1 == err2
  • 使用错误包的Is函数
errors.Is(err1, err2)

这两种方法都正确输出错误是相等的,因为err1err2都相同。

  • 指向相同的底层类型,即errorOne

  • 具有相同的底层值

我们在上面提到,使用Is函数优于使用相等运算符,因为它通过逐步解包第一个错误来检查相等性,并在每一步解包时与目标错误匹配。让我们看一个例子。

package main

import (
	"errors"
	"fmt"
)

type errorOne struct{}

func (e errorOne) Error() string {
	return "Error One happended"
}

func main() {
	err1 := errorOne{}

	err2 := do()

	if err1 == err2 {
		fmt.Println("Equality Operator: Both errors are equal")
	} else {
		fmt.Println("Equality Operator: Both errors are not equal")
	}

	if errors.Is(err2, err1) {
		fmt.Println("Is function: Both errors are equal")
	}
}

func do() error {
	return fmt.Errorf("E2: %w", errorOne{})
}

输出

Equality Operator: Both errors are not equal
Is function: Both errors are equal

上面的程序几乎与前一个程序相同,唯一的区别在于在do()函数中我们也包装了错误。

return fmt.Errorf("E2: %w", errorOne{})
  • 相等运算符输出
Equality Operator: Both errors are not equal
  • Is函数输出时
Is function: Both errors are equal

这是因为返回的err2包装了一个errorOne实例,而相等运算符无法捕获,但Is函数可以捕获。

在 Golang 中比较浮点数

来源:golangbyexample.com/comparing-floating-point-numbers-go/

目录

** 引言

  • 比较相同浮点类型

  • 比较不同浮点类型

  • 比较相同浮点类型的容差

  • 使用 math.big 包进行比较

引言

Go 语言有两种浮点类型

  • float32

  • float64

比较相同浮点类型

可以使用 Go 的==运算符比较两个浮点数,前提是它们是相同的浮点类型。请看下面的例子

package main

import "fmt"

func main() {
    a := 3.14
    b := 3.14
    if a == b {
        fmt.Println("Same")
    } else {
        fmt.Println("Not Same")
    }

    a = 3.142
    b = 3.14
    if a == b {
        fmt.Println("Same")
    } else {
        fmt.Println("Not Same")
    }
}

输出:

Same
Not Same

比较不同浮点类型

比较 float32 和 float64 将导致编译错误

package main

import "fmt"

func main() {

    var a float32
    var b float64
    if a == b {
        fmt.Println("Same")
    } else {
        fmt.Println("Not Same")
    }
}

输出:

invalid operation: a == b (mismatched types float32 and float64)

比较相同浮点类型的容差

如果在比较浮点类型时可接受某种容差,则可以使用以下方法

package main

import (
    "fmt"
    "math"
)

func main() {
    withTolerane(3.14, 3.141)
    withTolerane(3.14, 3.142)
}

func withTolerane(a, b float64) {
    tolerance := 0.001
    if diff := math.Abs(a - b); diff < tolerance {
        fmt.Printf("When a=%f and b =%f => Nearly same by tolerance\n", a, b)
    } else {
        fmt.Printf("When a=%f and b=%f => Not same Even by Tolerance\n", a, b)
    }
}

输出:

When a=3.140000 and b =3.141000 => Nearly same by tolerance
When a=3.140000 and b=3.142000 => Not same Even by Tolerance

使用 math.big 包进行比较

big 包支持大数,并支持整型、有理数和浮点数。它有一个比较方法,可以用来比较两个浮点数

package main

import (
    "fmt"
    "math/big"
)

func main() {
    a := 3.1432
    b := 3.1456
    // compare a to b
    result := big.NewFloat(a).Cmp(big.NewFloat(b))

    // -1 if a < b
    if result < 0 {
        fmt.Println("a less than b")
    }

    // 0 if a == b
    if result == 0 {
        fmt.Println("a  equals to b")
    }

    // +1 if a > b
    if result > 0 {
        fmt.Println("a 1 more than b")
    }
}

输出:

a less than b

Go(Golang)中的编译时多态性

来源:golangbyexample.com/compile-time-polymorphism-go/

在编译时多态中,调用在编译时由编译器解析。编译时多态的一些形式是

  • 方法/函数重载:存在多个同名但具有不同签名或可能不同返回类型的方法/函数。

  • 运算符重载:相同的运算符用于不同数据类型的操作。

Go 不支持方法重载。例如,以下程序演示了 Go 不支持方法重载。

package main

type maths struct{}

func (m *maths) add(a, b int) int {
    return a + b
}

func (m *maths) add(a, b, c int) int {
    return a + b + c
}

func main() {
   m := &maths{}
}

输出:

(*maths).add redeclared in this block
        previous declaration at ./main.go:5:6

Go 也不支持运算符重载。原因在于 Go 的常见问题解答中说明了——golang.org/doc/faq#overloading

如果方法分发不需要进行类型匹配,则会简化。与其他语言的经验告诉我们,拥有多个同名但具有不同签名的方法有时是有用的,但在实践中也可能令人困惑且脆弱。在 Go 的类型系统中,仅通过名称匹配并要求类型一致性是一个重要的简化决策。

关于运算符重载,它似乎更多是一种便利,而非绝对的要求。没有它,事情更简单。

现在的问题是,Go 中是否有替代方法进行方法重载。这就是 Go 中的 可变参数函数 进入画面的地方。见以下程序。

package main

import "fmt"

type maths struct{}

func (m *maths) add(numbers ...int) int {
    result := 0
    for _, num := range numbers {
        result += num
    }
    return result
}

func main() {
    m := &maths{}

    fmt.Printf("Result: %d\n", m.add(2, 3))
    fmt.Printf("Result: %d\n", m.add(2, 3, 4))
}

输出:

Result: 5
Result: 9

结论

Go 不直接支持方法/函数/运算符重载,但可变参数函数提供了一种实现相同功能的方法,尽管代码复杂性增加。

Go 中的组合设计模式(GoLang)

来源:golangbyexample.com/composite-design-pattern-golang/

注意:对理解其他所有设计模式在 GO 中的实现感兴趣。请参阅这个完整参考 – Go 语言中的所有设计模式

目录

** 定义:

  • 何时使用

  • UML 图

  • 映射

  • 实际示例

定义:

这是一种结构设计模式。当我们希望一组称为“组合”的对象以与单个对象相同的方式处理时,组合设计模式就会被使用。它属于结构设计模式,因为它允许您将对象组合成树结构。树结构中的每个个体对象可以以相同的方式处理,无论它们是复杂的还是原始的。

让我们通过操作系统的文件系统示例来理解它。在文件系统中,有两种类型的对象文件文件夹。在某些情况下,文件和文件夹被视为相同的方式。随着我们深入,情况会更加清晰。

何时使用

  • 组合设计模式在组合对象和个体对象需要从客户端角度以相同方式处理的情况下使用是有意义的。

– 在我们上面的文件系统示例中,假设需要执行特定关键字的搜索操作。现在,这个搜索操作适用于文件文件夹。对于文件,它只会查看文件的内容;而对于文件夹,它会遍历该文件夹层次结构中的所有文件以找到该关键字。

  • 当组合对象和个体对象形成树状结构时使用此模式

– 在我们的示例中,文件文件夹确实形成了一个树结构

UML 图

  • 组件 – 它是定义组合叶子对象的公共操作的接口

  • 组合 – 它实现了组件接口,并嵌入了一组子组件

  • 叶子 – 它是树中的原始对象。它也实现了组件接口

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/a2112b0fd53adcf02fc6a03a4e0c5226.png)

下面是与上述示例对应的映射 UML 图

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/241bbd461e5145959cbcba02e11dca09.png)

映射

下表表示从 UML 图中的参与者到代码中实际实现的参与者的映射。

组件接口 component.go
组合 folder.go
叶子 file.go
客户端 main.go

实际示例

在下面的示例中,组件是接口,而文件文件夹实现了这个接口

component.go

package main

type component interface {
    search(string)
}

folder.go

package main

import "fmt"

type folder struct {
    components []component
    name       string
}

func (f *folder) search(keyword string) {
    fmt.Printf("Serching recursively for keyword %s in folder %s\n", keyword, f.name)
    for _, composite := range f.components {
        composite.search(keyword)
    }
}

func (f *folder) add(c component) {
    f.components = append(f.components, c)
}

file.go

package main

import "fmt"

type file struct {
    name string
}

func (f *file) search(keyword string) {
    fmt.Printf("Searching for keyword %s in file %s\n", keyword, f.name)
}

func (f *file) getName() string {
    return f.name
}

main.go

package main

func main() {
    file1 := &file{name: "File1"}
    file2 := &file{name: "File2"}
    file3 := &file{name: "File3"}
    folder1 := &folder{
        name: "Folder1",
    }
    folder1.add(file1)
    folder2 := &folder{
        name: "Folder2",
    }
    folder2.add(file2)
    folder2.add(file3)
    folder2.add(folder1)
    folder2.search("rose")
}

输出:

Serching recursively for keyword rose in folder Folder2
Searching for keyword rose in file File2
Searching for keyword rose in file File3
Serching recursively for keyword rose in folder Folder1
Searching for keyword rose in file File1

在 Go(Golang)中正则表达式的串联(AND)

来源:golangbyexample.com/concatenation-regex-golang/

目录

  • 概述

  • 程序

概述

它类似于AND运算符。例如,如果有两个正则表达式r1r2,它们的串联将表示如下。

r1r2

它将匹配正则表达式r1后跟r2r1r2都应为有效的正则表达式。基本上,如果字符串s匹配正则表达式r1,并且字符串t匹配正则表达式r2,那么字符串st将匹配r1r2

程序

这是相应的示例程序

package main

import (
	"fmt"
	"regexp"
)

func main() {
	first := "abc"
	second := "xyz"
	sampleRegex := regexp.MustCompile(first + second)

	match := sampleRegex.Match([]byte("abcxyz"))
	fmt.Println(match)

	match = sampleRegex.Match([]byte("abc"))
	fmt.Println(match)

	match = sampleRegex.Match([]byte("xyz"))
	fmt.Println(match)

	match = sampleRegex.Match([]byte("abd"))
	fmt.Println(match)
}

输出

true
false
false
false

它只匹配

abcxyz

我们首先使用MustCompile函数编译给定的正则表达式。如果给定的正则表达式无效,则该函数会引发恐慌。在成功编译给定的正则表达式后,它将返回regexp结构的实例。

sampleRegexp := regexp.MustCompile(first + second)

我们可以在正则表达式实例上调用Match方法,将给定模式与正则表达式进行匹配。如果正则表达式与输入字符串匹配,则返回 true,否则返回 false。我们需要将输入字符串的字节传递给此方法。

match := sampleRegexp.Match([]byte("abcxyz"))

串联也可以在三个以上的正则表达式之间进行。以下是一个示例

package main

import (
    "fmt"
    "regexp"
)
func main() {
    first := "abc"
    second := "xyz"
    third := "123"
    sampleRegex := regexp.MustCompile(first + second + third)

    match := sampleRegex.Match([]byte("abcxyz123"))
    fmt.Println(match)

    match = sampleRegex.Match([]byte("abc"))
    fmt.Println(match)

    match = sampleRegex.Match([]byte("xyz"))
    fmt.Println(match)

    match = sampleRegex.Match([]byte("abcxyz"))
    fmt.Println(match)

    match = sampleRegex.Match([]byte("abd"))
    fmt.Println(match)
}

输出

true
false
false
false
false

它只匹配

abcxyz123

此外,查看我们的 Golang 进阶教程系列 – Golang 进阶教程*

Go 中的常量结构体 (Golang)

来源:golangbyexample.com/const-struct-go/

目录

概述

  • 示例

概述

Go 仅支持四种常量类型

  • 数字(int, int64, float, float64, complex128 等)

  • 字符串

  • 字符或符文

  • 布尔值

它不支持常量结构体。因此,下面的程序将引发编译错误。

示例

package main
import "fmt"
type employee struct {
    name string
    age  int
}
func main() {
    const e = employee{
        name: "John",
        age:  21,
    }
    fmt.Println(e)
}

输出

const initializer employee literal is not a constant

然而,解决方法是创建一个可以返回结构体的函数。这在某种程度上满足了常量结构体的目的,因为它每次都会返回相同的结构体。

package main
import "fmt"
type employee struct {
    name string
    age  int
}
func main() {
    e := baseEmployee()
    fmt.Println(e)
}
func baseEmployee() employee {
    return employee{
        name: "Unnamed",
        age:  0,
    }
}

输出

{Unnamed 0}

Go (Golang) 中的常量数组或切片

来源:golangbyexample.com/constant-array-golang/

目录

  • 概述

  • 示例

概述

Go 只支持四种常量类型

  • 数值(int, int64, float, float64, complex128 等)

  • 字符串

  • 字符或字符集

  • 布尔值

Go 不支持常量数组或切片。这是因为在 Go 中常量值在编译时计算,而数组或切片始终在运行时求值。因此,下面的程序会引发编译错误。

示例

package main
func main() {
	const e = [1]int{1}
}

输出

const initializer [1]int literal is not a constant

Go(Golang)中的常量

来源:golangbyexample.com/constant-golang/

这是 golang 综合教程系列的第九章。有关该系列其他章节,请参考此链接 – Golang 综合教程系列

下一个教程for 循环

上一个教程函数

现在让我们查看当前的教程。以下是当前教程的目录。

目录

概述

  • 重要点

  • 类型和未类型常量

    • 类型常量

    • 未类型常量

  • 命名约定

  • 全局常量

  • 常量类型

    • 字符串常量

    • 数字常量

    • 布尔常量

    • 字符常量

  • 结论 * * # 概述

常量是任何不会改变其值的东西。在 Go 中,const 可以是字符串、数字、布尔和字符类型。

常量可以使用const关键字声明。需要注意的一个重要点是,在声明常量时必须赋值。这与变量不同,变量的值可以稍后赋值。

  • 声明一个const并指定类型 – 它以const关键字开头,后面是名称,然后是类型。值必须立即赋值,如上所述。
const c string = "circle"
  • 声明一个不指定类型的 const – 一个没有指定类型的 const 是一个未类型化常量。我们稍后将深入了解类型常量和未类型常量。现在,重要的是要知道未指定类型的 const 具有默认的隐藏类型。常量一旦以任何方式(直接初始化、传递给函数等)分配给变量,就会被赋予类型。
const c = "circle"
  • 一次声明多个 const。
const (
  c = "circle"
  s = "square"
)

重要点

  • 常量变量在声明后不能重新赋值。例如,下面的代码将引发编译错误。
package main
func main() {
    const a int = 8
    a = 9
}

错误:

main.go:5:4: cannot assign to a
  • const 值必须在编译时已知。因此,const 值不能赋值给运行时评估的函数调用。在下面的程序中,a 是一个 const,其值应该在编译时可用,但函数getValue将在运行时调用,因此在编译时会引发错误。
package main
const name = "test"
func main() {
    const a = getValue()
}
func getValue() int {
    return 1
}

错误:

const initializer getValue() is not a constant
  • 在内部声明的常量如果与外部作用域中声明的常量同名,则会遮蔽外部作用域中的常量。
package main
import "fmt"
const a = 123
func main() {
    const a = 456
    fmt.Println(a)
}

输出:

456

类型化与未类型化常量

现在谈谈一个非常重要的话题。在 GO 中,常量的处理方式与任何其他语言不同。GO 具有非常强的类型系统,不允许不同类型之间的隐式转换。即使是相同的数字类型,没有显式转换也不允许进行操作。例如,你不能将 int32int64 值相加。要相加,必须将 int32 显式转换为 int64 或反之。然而,未类型化常量具有暂时逃避 GO 类型系统的灵活性,正如我们将在本文中看到的。

类型化常量

声明时指定类型的常量是类型化常量。例如,下面我们声明一个类型为 int32 的常量。

const a int32 = 8

这个常量 a 只能赋值给类型为 int32 的变量。如果你将其赋值给任何其他类型的变量,将会引发错误。请参见下面的程序进行说明。

package main

func main() {
    const a int32 = 8

    var i1 int32
    var i2 int64

    i1 = a
    i2 = a
}

输出:

cannot use a (type int32) as type int64 in assignment

未类型化常量

未类型化常量是类型未被指定的常量。GO 中的未类型化常量可以是命名的或未命名的。在这两种情况下,它都没有任何相关的类型。

未命名未类型化常量的示例。

123        //Default hidden type is int
"circle"   //Default hidden type is string
5.6\.       //Default hidden type is float64
true       //Default hidden type is bool
'a'        //Default hidden type is rune
3+5i       //Default hidden type is complex128

命名未类型化常量的示例。

const a = 123        //Default hidden type is int
const b = "circle"   //Default hidden type is string
const c = 5.6       //Default hidden type is float64
const d = true       //Default hidden type is bool
const e = 'a'        //Default hidden type is rune
const f = 3+5i       //Default hidden type is complex128

未类型化常量确实具有默认的隐藏类型。例如,下面的表格展示了数字、字符串、字符和布尔值的隐藏默认类型。

常量的默认隐藏类型

整数 int
浮点数 float64
复数 complex128
字符串 string
布尔值 bool
字符 int32 或 rune

当你使用 fmt.Printf 打印任何未类型化的常量时,它将打印默认的隐藏类型。请查看下面的程序和未命名与命名未类型化常量的输出。

package main

import "fmt"

func main() {
    //Unanamed untyped constant
    fmt.Printf("Type: %T Value: %v\n", 123, 123)
    fmt.Printf("Type: %T Value: %v\n", "circle", "circle")
    fmt.Printf("Type: %T Value: %v\n", 5.6, 5.6)
    fmt.Printf("Type: %T Value: %v\n", true, true)
    fmt.Printf("Type: %T Value: %v\n", 'a', 'a')
    fmt.Printf("Type: %T Value: %v\n", 3+5i, 3+5i)

    //Named untyped constant
    const a = 123      //Default hidden type is int
    const b = "circle" //Default hidden type is string
    const c = 5.6      //Default hidden type is float64
    const d = true     //Default hidden type is bool
    const e = 'a'      //Default hidden type is rune
    const f = 3 + 5i   //Default hidden type is complex128

    fmt.Println("")
    fmt.Printf("Type: %T Value: %v\n", a, a)
    fmt.Printf("Type: %T Value: %v\n", b, b)
    fmt.Printf("Type: %T Value: %v\n", c, c)
    fmt.Printf("Type: %T Value: %v\n", d, d)
    fmt.Printf("Type: %T Value: %v\n", e, e)
    fmt.Printf("Type: %T Value: %v\n", f, f)
}

输出:

Type: int Value: 123
Type: string Value: circle
Type: float64 Value: 5.6
Type: bool Value: true
Type: int32 Value: 97
Type: complex128 Value: (3+5i)

Type: int Value: 123
Type: string Value: circle
Type: float64 Value: 5.6
Type: bool Value: true
Type: int32 Value: 97
Type: complex128 Value: (3+5i)

上述程序打印 int32 而不是字符,因为字符是 int32 的别名。

命名或未命名常量的默认类型将成为它们赋值的变量的类型。例如,在下面的代码中,变量 a 将从未命名常量 123 的默认类型 int 中获得类型。

var a = 123

让我们看一个程序,说明上述所有未命名类型常量的要点。

package main
import "fmt"
func main() {
    //Untyped
    var u = 123      //Default hidden type is int
    var v = "circle" //Default hidden type is string
    var w = 5.6      //Default hidden type is float64
    var x = true     //Default hidden type is bool
    var y = 'a'      //Default hidden type is rune
    var z = 3 + 5i   //Default hidden type is complex128
    fmt.Printf("Type: %T Value: %v\n", u, u)
    fmt.Printf("Type: %T Value: %v\n", v, v)
    fmt.Printf("Type: %T Value: %v\n", w, w)
    fmt.Printf("Type: %T Value: %v\n", x, x)
    fmt.Printf("Type: %T Value: %v\n", y, y)
    fmt.Printf("Type: %T Value: %v\n", z, z)
}

输出

Type: int Value: 123
Type: string Value: circle
Type: float64 Value: 5.6
Type: bool Value: true
Type: int32 Value: 97
Type: complex128 Value: (3+5i)

现在我们想到的问题是未类型化常量有什么用。未类型化常量的作用在于,常量的类型将根据赋值的变量类型来决定。听起来令人困惑?让我们通过一个例子来看。

数学包中的 Pi 常量值声明如下。

const Pi = 3.14159265358979323846264338327950288419716939937510582097494459

请注意,类型未被指定,只具有一个隐藏的默认类型(这里是 float64)。让我们看一段代码。

package main
import (
    "fmt"
    "math"
)
func main() {
    var f1 float32
    var f2 float64
    f1 = math.Pi
    f2 = math.Pi

    fmt.Printf("Type: %T Value: %v\n", math.Pi, math.Pi)
    fmt.Printf("Type: %T Value: %v\n", f1, f1)
    fmt.Printf("Type: %T Value: %v\n", f2, f2)
}

输出

Type: float64 Value: 3.141592653589793
Type: float32 Value: 3.1415927
Type: float64 Value: 3.141592653589793

请注意上述程序。

  • 由于 math.Pi 常量的未类型化特性,它可以被赋值给 float32float64 类型的变量。在类型确定后,GO 中通常不允许这样做。

  • 当我们打印 math.Pi 的类型时,它打印默认类型,即 float64

根据使用情况,无类型常量可以分配给低精度类型(float32)或高精度类型(float64)。

命名约定

常量的命名约定与变量的命名约定相同。

  • 常量名称只能以字母或下划线开头。之后可以跟任意数量的字母、数字或下划线。

全局常量

与其他变量一样,如果常量在文件顶部的任何函数的作用域之外声明,则它将在包内全局可用。例如,在下面的程序中,name 将是一个在任何函数中都可用的全局常量。请注意,const name 在主包外不可用。为了使其在主包外可用,它必须以大写字母开头。

见下面的代码。它还展示了一个包内局部常量的示例。

package main

import "fmt"

const name = "test"

func main() {
    const a = 8
    fmt.Println(a)
    testGlobal()
}

func testGlobal() {
    fmt.Println(name)
    //The below line will give compiler error as a is a local constant
    //fmt.Println(a)

常量类型

常量可以有四种类型:

  • 数值

  • 字符串

  • 字符

  • 布尔型

字符串常量

在 Go 语言中,字符串常量以两种方式表示

  • 任何用双引号括起来的值

  • 任何用反引号括起来的值

下面的程序展示了一个示例

  • 有类型字符串常量

  • 无类型未命名字符串常量

  • 无类型命名字符串常量

package main

import "fmt"

func main() {
	type myString string

	//Typed String constant
	const aa string = "abc"
	var uu = aa
	fmt.Println("Untyped named string constant")
	fmt.Printf("uu: Type: %T Value: %v\n\nn", uu, uu)

	//Below line will raise a compilation error
	//var v myString = aa

	//Untyped named string constant
	const bb = "abc"
	var ww myString = bb
	var xx = bb
	fmt.Println("Untyped named string constant")
	fmt.Printf("ww: Type: %T Value: %v\n", ww, ww)
	fmt.Printf("xx: Type: %T Value: %v\n\n", xx, xx)

	//Untyped unnamed string constant
	var yy myString = "abc"
	var zz = "abc"
	fmt.Println("Untyped unnamed string constant")
	fmt.Printf("yy: Type: %T Value: %v\n", yy, yy)
	fmt.Printf("zz: Type: %T Value: %v\n", zz, zz)

}

输出:

Untyped named string constant
uu: Type: string Value: abc

nUntyped named string constant
ww: Type: main.myString Value: abc
xx: Type: string Value: abc

Untyped unnamed string constant
yy: Type: main.myString Value: abc
zz: Type: string Value: abc

在上述程序中,我们在代码中创建了一个新类型myString

type myString string

以上程序还展示了

  • 有类型字符串常量

  • 无类型未命名字符串常量

  • 无类型命名字符串常量

让我们理解它们及其行为

有类型字符串常量

定义如下

const aa string = "abc"

注意上面这行会导致编译错误。这是因为有类型字符串常量aastring类型。因此下面这一行会导致编译错误,因为它不能分配给myString类型的变量。

var v myString = aa

但有类型字符串常量可以分配给用var关键字创建的变量,如下所示

var uu = aa

无类型命名字符串常量

定义如下

const bb = "abc"

无类型命名字符串常量可以分配给myString类型的变量,以及用var关键字创建的变量,因为它是无类型的,所以常量的类型将根据所分配变量的类型来决定。

var ww myString = bb
var xx = bb

无类型未命名字符串常量

它是这样的

abc

无类型未命名字符串常量可以分配给myString类型的变量,以及用var关键字创建的变量,因为它是无类型的,所以常量的类型将根据所分配变量的类型来决定。

var yy myString = "abc"
var zz = "abc"

数值常量

数值常量进一步分为三种类型

  • 整数

  • 浮点数

  • 复数

一个无类型整数常量(命名和未命名)可以分配给int类型、float类型和complex类型。这是因为一个 int 值可以是 int、float 或 complex。例如,int 值123可以是

  • 一个值为 123 的int

  • 一个值为 123.0 的float

  • 一个虚部为 0 的complex

基于类似逻辑,未类型float常量可以赋值给所有floatscomplex类型,但不能赋值给integer,因为例如一个 float 5.3 不能是整数。

基于类似逻辑,未类型complex常量可以赋值给complex类型,但不能赋值给integerfloat,例如一个 float 5i+3 不能是integerfloat

见下面的程序,说明上述观点。在该程序中,我们有一个例子。

  • 类型整数常量

  • 未类型未命名整数常量

  • 未类型命名整数常量

package main

import "fmt"

func main() {
	//Typed int constant
	const aa int = 123
	var uu = aa
	fmt.Println("Typed named integer constant")
	fmt.Printf("uu: Type: %T Value: %v\n\n", uu, uu)

	//Below line will raise a compilation error
	//var v int32 = aa

	//Untyped named int constant
	const bb = 123
	var ww = bb
	var xx int32 = bb
	var yy float64 = bb
	var zz complex128 = bb
	fmt.Println("Untyped named integer constant")
	fmt.Printf("ww: Type: %T Value: %v\n", ww, ww)
	fmt.Printf("xx: Type: %T Value: %v\n", xx, xx)
	fmt.Printf("yy: Type: %T Value: %v\n", yy, yy)
	fmt.Printf("zz: Type: %T Value: %v\n\n", zz, zz)

	//Untyped unnamed int constant
	var ll = 123
	var mm int32 = 123
	var nn float64 = 123
	var oo complex128 = 123
	fmt.Println("Untyped unnamed integer constant")
	fmt.Printf("ll: Type: %T Value: %v\n", ll, ll)
	fmt.Printf("mm: Type: %T Value: %v\n", mm, mm)
	fmt.Printf("nn: Type: %T Value: %v\n", nn, nn)
	fmt.Printf("oo: Type: %T Value: %v\n", oo, oo)
}

输出

Typed named integer constant
uu: Type: int Value: 123

Untyped named integer constant
ww: Type: int Value: 123
xx: Type: int32 Value: 123
yy: Type: float64 Value: 123
zz: Type: complex128 Value: (123+0i)

Untyped unnamed integer constant
ll: Type: int Value: 123
mm: Type: int32 Value: 123
nn: Type: float64 Value: 123
oo: Type: complex128 Value: (123+0i)

现在上述程序展示了一个

  • 类型整数常量

  • 未类型未命名整数常量

  • 未类型命名整数常量

让我们理解每个常量及其行为

类型整数常量

定义如下

const aa int = 123

类型整数常量可以赋值给使用var关键字创建的变量,如下所示。

var uu = aa

当赋值给另一个int类型时,会引发编译错误。因此,下面的代码引发编译错误,因为aa变量已经是int类型。

var v int32 = aa

未类型命名整数常量

定义如下

const bb = 123

未类型命名整数常量可以赋值给任何int类型、任何float类型和任何complex数类型,以及使用var关键字创建的任何变量。因此,下面的代码可以正常工作。

var ww = bb
var xx int32 = bb
var yy float64 = bb
var zz complex128 = bb

未类型未命名整数常量

如下所示

123

未类型命名整数常量可以赋值给任何int类型、任何float类型和任何complex数类型,以及使用var关键字创建的任何变量。因此,下面的代码可以正常工作。

var ww = 123
var xx int32 = 123
var yy float64 = 123
var zz complex128 = 123

数值表达式

由于未类型常量的特性,不同数值常量类型可以混合搭配形成表达式。

package main
import "fmt"
func main() {
    var p = 5.2 / 3
    fmt.Printf("p: Type: %T Value: %v\n", p, p)
}

输出:

p: Type: float64 Value: 1.7333333333333334

布尔常量

有两个未类型布尔常量truefalse。下面是一个展示布尔常量的程序。

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

字符常量

以下是演示字符常量的程序。

package main

import "fmt"

func main() {
	type myChar int32

	//Typed character constant
	const aa int32 = 'a'
	var uu = aa
	fmt.Println("Untyped unnamed character 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 character constant
	const bb = 'a'

	var ww myChar = bb
	var xx = bb
	fmt.Println("Untyped named character constant")
	fmt.Printf("ww: Type: %T Value: %v\n", ww, ww)
	fmt.Printf("xx: Type: %T Value: %v\n\n", xx, xx)

	//Untyped unnamed character constant
	var yy myChar = 'a'
	var zz = 'a'
	fmt.Println("Untyped unnamed character constant")
	fmt.Printf("yy: Type: %T Value: %v\n", yy, yy)
	fmt.Printf("zz: Type: %T Value: %v\n", zz, zz)
}

输出:

Untyped unnamed character constant
uu: Type: int32 Value: 97

Untyped named character constant
ww: Type: main.myChar Value: 97
xx: Type: int32 Value: 97

Untyped unnamed character constant
yy: Type: main.myChar Value: 97
zz: Type: int32 Value: 97

在上述程序中,我们创建了一个新类型myChar

type myChar int32

同时,上述程序展示了示例

  • 类型化字符常量

  • 未类型化的无名字符常量

  • 未类型化的有名字符常量

让我们理解它们每一个及其行为

类型化字符常量

它的定义如下

const aa int32 = 'a'

上述内容中请注意,下面的行将导致编译错误。这是因为变量aaint32类型。因此,下面的行将导致编译错误,因为它不能赋值给myChar类型的变量。

var v myChar = aa

但是,类型化字符串常量可以赋值给使用var关键字创建的变量,如下所示。

var uu = aa

未类型化的有名字符常量

它的定义如下

const bb = 'a'

未类型化的有名字符串常量可以赋值给myChar类型的变量以及使用var关键字创建的变量,因为它是无类型的,所以常量的类型将根据所赋值的变量类型决定。

var ww myChar = bb
var xx = bb

未类型化的无名字符常量

它如下所示

'a'

未类型化的无名字符串常量可以赋值给myChar类型的变量以及使用var关键字创建的变量,因为它是无类型的,所以常量的类型将根据所赋值的变量类型决定。

var yy myChar = 'a'
var zz = 'a'

结论

这就是关于 Golang 中 const 关键字的全部内容。希望你喜欢这篇文章。请在评论中分享反馈。

下一个教程For Loop

上一个教程Functions

posted @ 2024-10-19 08:37  绝不原创的飞龙  阅读(2)  评论(0编辑  收藏  举报