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() 方法。
相关推荐
bobz9653 分钟前
ovs patch port 对比 veth pair
后端
Asthenia041213 分钟前
Java受检异常与非受检异常分析
后端
uhakadotcom27 分钟前
快速开始使用 n8n
后端·面试·github
JavaGuide34 分钟前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz96544 分钟前
qemu 网络使用基础
后端
Asthenia04121 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04121 小时前
Spring 启动流程:比喻表达
后端
Asthenia04122 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua2 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
致心2 小时前
记一次debian安装mariadb(带有迁移数据)
后端