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() 方法。
相关推荐
陌小呆^O^9 分钟前
Cmakelist.txt之win-c-udp-server
c语言·开发语言·udp
计算机毕设指导615 分钟前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study16 分钟前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言
时光の尘31 分钟前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
paopaokaka_luck32 分钟前
[371]基于springboot的高校实习管理系统
java·spring boot·后端
以后不吃煲仔饭44 分钟前
Java基础夯实——2.7 线程上下文切换
java·开发语言
进阶的架构师1 小时前
2024年Java面试题及答案整理(1000+面试题附答案解析)
java·开发语言
前端拾光者1 小时前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
程序猿阿伟1 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链
傻啦嘿哟1 小时前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel