0%

错误示范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"github.com/go-playground/validator/v10"
)

type Person struct {
Name string `validate:"required"`
State int `validate:"required,oneof=0 1"`
}

func main() {
validate := validator.New()

p := Person{
Name: "abcc",
State: 0,
}
err := validate.Struct(p)

// Key: 'Person.State' Error:Field validation for 'State' failed on the 'required' tag
fmt.Println(err)
}

在这个例子中,虽然我们的 State 字段传入了值,但是验证却不通过。

正确示范

在使用 go-playground/validator/v10 验证一些空值的时候,需要使用指针类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
"github.com/go-playground/validator/v10"
)

type Person struct {
Name string `validate:"required"`
// 使用指针类型
State *int `validate:"required,oneof=0 1"`
}

func main() {
validate := validator.New()

state := 0

p := Person{
Name: "abcc",
State: &state, // 传入指针
}
err := validate.Struct(p)

// <nil>
fmt.Println(err)
}

今天偶然刷到一个漫画家蔡志忠的访谈视频,下面是他的原话:

为什么努力是没有用的?老师或父母老是说努力努力就会走到巅峰——才怪。如果这样,不是所有人都走上巅峰了吗?没有人开始不努力,为什么后来不努力,因为努力没有效果。人生不是走斜坡,你持续走就可以走到巅峰;人生像走阶梯,每一阶有每一阶的难点,学物理有物理的难点,学漫画有漫画的难点,你没有克服难点,再怎么努力都是原地跳。所以当你克服难点,你跳上去就不会下来了。就像你学会语文,即使你十年不讲,碰到状况就会讲;就像学脚踏车,十年没骑,碰到脚踏车一上去就可以上手一样。

当然,虽然蔡老师表面上说努力没有用,但蔡志忠老师的意思应该是「不能盲目的努力」,不能用战术上的勤奋来掩盖战略上的懒惰。

其实这个观点前段时间在池建强老师的直播间也听到,只不过当时好像并没有真的懂了,今天看到蔡志忠老是说的 人生像走阶梯,每一阶有每一阶的难点,学物理有物理的难点,学漫画有漫画的难点,你没有克服难点,再怎么努力都是原地跳 之后,再结合一下自己以往经历,总算是懂了。

下面就结合自己所在领域聊一下自己的理解吧。

过去几年里,自己虽然作为计算机科班出身的人,但是一直都没有认真好好学那些计算机的基础课程,也在找工作时候刻意躲开需要考查算法的岗位,因为自己懂得不多,而且总是觉得太难而没有去学。

不过去年大病一场之后,自己也想明白了,如果自己一直都是这样,永远也突破不了自己,因此开始持续刷了 6 个月的 leetcode,总算把常用的数据结构算法熟悉了一些。

虽然还不到非常熟练的地步,但是也比之前的自己好了好多倍了。

计算机领域的难点

就像蔡志忠老师说的,其实计算机领域中也有其难点,如下就是:

  • 数据结构算法
  • 计算机网络
  • 操作系统
  • 计算机组成原理
  • 编译原理

除了这些,软件设计、重构、面向对象设计这些也是其中的难点,不过上面这几个能在我们面临一些具体问题的时候会更有用,而软件设计这些则对我们写好代码、写出可维护、可扩展的代码非常有用。

作为科班出身的我,对上面都不是很熟悉。更不要说,目前中国程序员行业还有很多转行过来的人,这些人有很多可能连cpu、内存、磁盘这些都不太懂。

但正是这些东西,可以让我们更快地去解决技术上碰到的一些难题。比如很简单的一些性能上问题,需要多次搜索的时候,构建出一个 hash 比我们每次去做 O(n) 的循环效率高数百倍甚至更高。

自己过去几年做了什么

过去几年里,虽然自己一直有在学习,但是正如蔡志忠老师说的,我那些 努力 是没有用的,因为自己学的都是简单的东西, 比如一门新的语言,然后用这门新语言写一些 demo 之类的,虽然挺好玩,但说实话,并没有感觉自己能力上有太大的突破。

然后认真看了自己平时用的框架的源代码,看过了好几遍,里面的设计、实现都有比较深入的了解,但是还是那句话, 看了这些除了能熟练应对工作中技术上的问题之外,自己的能力上好像并没有太大的突破,有种学了很多东西依然原地踏步的感觉。

总之,就是没有突破自己目前所在的层级,虽然有时候自己看起来好像挺牛逼,好多问题都能很快解决,但是平时做的东西,换个人做也是可以的, 只不过给我做的话,我会考虑更多性能、代码整洁之类的问题,然后可以比较快地解决问题。本质上来讲自己跟他们是一样的,只是好了一点而已。

做难而正确的事

今年年初的时候,看到左晖的一个访谈,其实是一本书,叫《做难而正确的事》。里面有个观点就是,如果让他选择一件容易和一件难的事,他会选择那件难的事,因为可能成功率更高。

那段时间刚好在刷 leetcode,代码随想录那个网站里面那些 leetcode 题目从头到尾刷了一遍,因为自己当初没学好,因此刷起来还是有点困难,但还是坚持下来了。

现在的自己也依然在坚持做那些难而正确的事,老老实实学好这些计算机领域的基础课程。在这个过程,其实也收获了很多,也慢慢走在了上台阶的路上。

好了,该说的说完了,太久没写非技术的文章了,写了一下发现自己的作文水平好像不太好,那就这样吧。

lo

samber/lo 是一个 go 里面类似 js 里的 lodash 的库,可以让我们很方便地对切片做一些诸如 filter、unique 等操作的库。

安装

1
go get github.com/samber/lo@v1

最新版本支持 go1.18+,也就是可以使用泛型,这样就给开发者带来了更大的便利了。

示例

  1. Filter:过滤出符合条件的元素
1
2
3
4
even := lo.Filter[int]([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
})
// []int{2, 4}
  1. Map:对每一个元素进行处理,返回新的切片
1
2
3
4
lo.Map[int64, string]([]int64{1, 2, 3, 4}, func(x int64, _ int) string {
return strconv.FormatInt(x, 10)
})
// []string{"1", "2", "3", "4"}
  1. Uniq:去重
1
2
uniqValues := lo.Uniq[int]([]int{1, 2, 2, 1})
// []int{1, 2}
  1. Chunk:分组
1
2
lo.Chunk[int]([]int{0, 1, 2, 3, 4, 5}, 2)
// [][]int{{0, 1}, {2, 3}, {4, 5}}

mergo

imdario/mergo 是一个可以让你合并结构体和 map 的库。

安装

1
go get github.com/imdario/mergo

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
"fmt"
"github.com/imdario/mergo"
)

type Foo struct {
A string
B int64
}

func main() {
src := Foo{
A: "one",
B: 2,
}
dest := Foo{
A: "two",
}
mergo.Merge(&dest, src)
fmt.Println(dest)
// Will print
// {two 2}
}

color

color 是一个可以让你输出带颜色文本的库。

安装

1
go get github.com/fatih/color

示例

  1. 输出到控制台
1
2
3
4
5
6
7
8
9
// 这会直接输出到控制台
color.Cyan("Prints text in cyan.")

// 每个调用末尾会自动加上换行
color.Blue("Prints %s in blue.", "text")

// 这种调用使用默认的背景色
color.Red("We have red")
color.Magenta("And many others ..")

效果:

  1. 混合样式:颜色 + 粗体/斜体、下划线等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个新的 color 对象,然后加上 “下划线” 样式
c := color.New(color.FgCyan).Add(color.Underline) // 下划线
c.Println("Prints cyan text with an underline.")

// 创建一个新的 color 对象,颜色为 FgCyan,字体为粗体
d := color.New(color.FgCyan, color.Bold) // 粗体
d.Printf("This prints bold cyan %s\n", "too!.")

// 创建一个颜色为 FgRed 的 color 对象
red := color.New(color.FgRed)

// 在这个颜色为 FgRed 的 color 对象的基础上,加上 “粗体” 的样式
boldRed := red.Add(color.Bold) // 红色 + 粗体
boldRed.Println("This will print text in bold red.")

// 在 红色 + 粗体 的 color 对象的基础上,加上 “白色背景” 的样式
whiteBackground := red.Add(color.BgWhite) // 红色 + 粗体 + 白色背景
whiteBackground.Println("Red text with white background.")

效果:

  1. 使用自定义的输出方向(实现了 io.Writer 即可):比如文件等
1
2
3
4
5
// 使用自定义的 `io.Writer` 保存 color 的输出
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")

blue := color.New(color.FgBlue)
blue.Fprint(writer, "This will print text in blue.")
  1. 自定义 print 函数:PrintFunc
1
2
3
4
5
6
7
8
// 创建一个新的自定义的 print 函数
red := color.New(color.FgRed).PrintfFunc()
red("Warning")
red("Error: %s", err)

// 创建自定义打印函数,并且添加 粗体 + 绿色 样式
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("Don't forget this...")
  1. 自定义 fprint 函数:FprintFunc
1
2
3
4
5
6
7
// 创建一个自定义的 fprint 函数
blue := color.New(color.FgBlue).FprintfFunc()
blue(myWriter, "important notice: %s", stars)

// 创建一个自定义的 fprint 函数,并且 粗体 + 绿色 字体
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
success(myWriter, "Don't forget this...")
  1. 无颜色字符串里面穿插有颜色的字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// SprintFunc 调用的效果是返回一个字符串,而不是直接输出了
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))

info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
fmt.Printf("This %s rocks!\n", info("package"))

// 也可以直接使用 helper 函数
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")

// windows 也支持
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
  1. 在现有的代码上使用
1
2
3
4
5
6
7
8
9
10
11
12
13
// 在 `color.Set()` 和 `color.Unset()` 之间的代码会有颜色
color.Set(color.FgYellow)

fmt.Println("Existing text will now be in yellow")
fmt.Printf("This one %s\n", "too")

color.Unset() // 需要手动调用 Unset,否则后续代码输出都会有颜色

// 也可以添加其他样式,这里加了 粗体
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // 函数结束的时候 Unset

fmt.Println("All text will now be bold magenta.")

禁用颜色

1
color.NoColor = true

其他解决方案

  1. gookit/color

  2. lucasb-eyer/go-colorful