GO Practise

文章目录

练习:切片

实现 Pic。它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx,元素类型为 uint8 的切片。当你运行此程序时,它会将每个整数解释为灰度值 (好吧,其实是蓝度值)并显示它所对应的图像。

图像的解析式由你来定。几个有趣的函数包括 (x+y)/2x*yx^yx*log(y)x%(y+1)

(提示:需要使用循环来分配 [][]uint8 中的每个 []uint8。)

(请使用 uint8(intValue) 在类型之间转换;你可能会用到 math 包中的函数。)

go 复制代码
package main

import (
    "math"
    "golang.org/x/tour/pic"
)

func Pic(dx, dy int) [][]uint8 {
    // 创建一个二维切片
    result := make([][]uint8, dy)
    for y := 0; y < dy; y++ {
        // 为每个 y 创建一个长度为 dx 的切片
        row := make([]uint8, dx)
        for x := 0; x < dx; x++ {
            // 使用一个公式来生成像素值,这里使用 x * log(y+1) 来生成图像
            row[x] = uint8(x * int(math.Log(float64(y+1))))
        }
        // 将该行添加到二维切片中
        result[y] = row
    }
    return result
}

func main() {
    pic.Show(Pic)
}
练习:映射

实现 WordCount。它应当返回一个映射,其中包含字符串 s 中每个"单词"的个数。 函数 wc.Test 会为此函数执行一系列测试用例,并输出成功还是失败。

go 复制代码
package main

import (
    "strings"
    "golang.org/x/tour/wc"
)

func WordCount(s string) map[string]int {
    // 创建一个空的 map 来存储单词和它们的计数
    wordMap := make(map[string]int)

    // 使用 strings.Fields 将字符串切分成单词
    words := strings.Fields(s)

    // 遍历单词并更新 map 中对应单词的计数
    for _, word := range words {
        wordMap[word]++
    }

    return wordMap
}

func main() {
    // 测试函数
    wc.Test(WordCount)
}
练习:斐波纳契闭包

让我们用函数做些好玩的。

实现一个 fibonacci 函数,它返回一个函数(闭包),该闭包返回一个斐波纳契数列 (0, 1, 1, 2, 3, 5, ...)。

go 复制代码
package main

import "fmt"

// 返回一个函数,生成斐波纳契数列
func fibonacci() func() int {
    // 初始化前两个斐波纳契数
    a, b := 0, 1
    return func() int {
        // 返回当前的 a 值(即斐波纳契数列的下一个数)
        fib := a
        // 更新斐波纳契数列,a = b,b = a + b
        a, b = b, a+b
        return fib
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        // 每次调用 f(),返回下一个斐波纳契数
        fmt.Println(f())
    }
}
练习:Stringer

通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。 例如,IPAddr{1, 2, 3, 4} 应当打印为 "1.2.3.4"。

go 复制代码
package main

import (
    "fmt"
)

// 定义 IPAddr 类型,它是一个包含 4 个字节的数组
type IPAddr [4]byte

// 实现 fmt.Stringer 接口
func (ip IPAddr) String() string {
    // 返回通过 fmt.Sprintf 格式化后的点号分隔的 IP 地址
    return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func main() {
    // 定义一个 IPAddr 类型的变量并打印
    hosts := map[string]IPAddr{
        "loopback": {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    
    for name, ip := range hosts {
        fmt.Printf("%v: %v\n", name, ip)
    }
}
shell 复制代码
googleDNS: 8.8.8.8
loopback: 127.0.0.1
练习:错误

Sqrt 函数,修改它使其返回 error 值。

Sqrt 接受到一个负数时,应当返回一个非 nil 的错误值。复数同样也不被支持。

创建一个新的类型

复制代码
type ErrNegativeSqrt float64

并为其实现

复制代码
func (e ErrNegativeSqrt) Error() string

方法使其拥有 error 值,通过 ErrNegativeSqrt(-2).Error() 调用该方法应返回 "cannot Sqrt negative number: -2"

注意:Error 方法内调用 fmt.Sprint(e) 会让程序陷入死循环。可以通过先转换 e 来避免这个问题:fmt.Sprint(float64(e))。这是为什么呢?

修改 Sqrt 函数,使其接受一个负数时,返回 ErrNegativeSqrt 值。

go 复制代码
package main

import (
    "fmt"
    "math"
)

// 创建自定义错误类型
type ErrNegativeSqrt float64

// 实现 error 接口的 Error 方法
func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

// 实现 Sqrt 函数
func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, ErrNegativeSqrt(x)
    }
    // 计算平方根
    return math.Sqrt(x), nil
}

func main() {
    // 测试 Sqrt 函数
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}
shell 复制代码
- 代码解析:
ErrNegativeSqrt 类型:我们定义了一个新的类型 ErrNegativeSqrt,它是 float64 类型的别名。
Error 方法:为 ErrNegativeSqrt 类型实现了 Error() 方法。在该方法中,使用 fmt.Sprintf 来格式化错误消息。要注意的是,直接调用 fmt.Sprint(e) 会导致死循环,因为 fmt.Sprint(e) 会试图再次调用 Error() 方法,导致无限递归。因此,需要先将 e 转换为 float64 类型,再使用 fmt.Sprint(float64(e)) 格式化输出。
Sqrt 函数:在 Sqrt 函数中,我们首先检查 x 是否为负数,如果是负数,则返回自定义的 ErrNegativeSqrt 错误。否则,使用 math.Sqrt 计算并返回平方根。
- 输出示例:
1.4142135623730951 <nil>
0 cannot Sqrt negative number: -2
- 为什么直接 fmt.Sprint(e) 会导致死循环?
因为 fmt.Sprint(e) 会隐式调用 e.Error() 方法。如果你在 Error() 方法内再次调用 fmt.Sprint(e),它又会再次调用 Error(),从而陷入无限递归。因此,我们在 Error() 方法中使用 fmt.Sprint(float64(e)),确保先将 e 转换为 float64,避免再次调用 Error() 方法。
相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
是苏浙3 小时前
JDK17新增特性
java·开发语言
不光头强3 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp6 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多6 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood6 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员6 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai
zs宝来了7 小时前
AQS详解
java·开发语言·jvm