Article
Golang 基础 2
标准输入输出
os 标准库使用:stdin 和 stdout
package main
import (
"os"
"syscall"
)
func main() {
// 使用 syscall.Stdin(值为0)创建标准输入文件
stdin := os.NewFile(uintptr(syscall.Stdin), "/dev/stdin")
defer stdin.Close()
// 现在可以使用 stdin 进行读取,例如:
var buf [100]byte
n, _ := stdin.Read(buf[:])
os.Stdout.Write(buf[:n])
}

os 本身在初始化时就用 NewFile 创建了 Stdout、Stdin、Stderr 这三个全局变量,使得我们能够直接使用它们。
也就是所谓的文件描述符
os.Stdin - 标准输入
os.Stdout - 标准输出
os.Stderr - 标准错误
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
bufio
bufio 提供了可缓冲的输出方法,它可以先将数据写入到内存中,积累到了一定阈值后再输出到指定的 Writer 中,默认缓冲区大小是 4kB,在文件 IO, 网络 IO 的时候建议使用这个包。
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
writer.WriteString("hello world!")
}
类似重载
为什么说是“类似重载”呢,因为golang里不存在函数重载的概念,这也很符合golang追求少就是多的设计哲学。
但是,在golang里面,可以使用两个类似的方法,但是接收者类型不同的方式,来实现类似“函数重载”的效果。
type A struct{}
func (a A) Do(x int) {}
type B struct{}
func (b B) Do(x, y string) {}
// 合法,因为接受者类型不同
所以:
- Go不支持函数重载,无论寒暑签名是否相同,都不能在同一个包内定义同名函数。
- 函数签名相同只会导致重复定义错误,不能达到重载的效果。
- 如果需要类似重载的行为,可以使用不同的函数名、可变参数、接口、泛型等方式来实现。
匿名函数
下列的main函数内的函数没有名字,所以我们只能在它的函数体后经跟着括号来进行调用。
func main() {
func(a, b int) int {
return a + b
}(1, 2)
}
我们在对结构体切片进行排序的时候会用到这个写法。
type Person struct {
Name string
Age int
Salary float64
}
func main() {
people := []Person{
{Name: "Alice", Age: 25, Salary: 5000.0},
{Name: "Bob", Age: 30, Salary: 6000.0},
{Name: "Charlie", Age: 28, Salary: 5500.0},
}
slices.SortFunc(people, func(p1 Person, p2 Person) int {
if p1.Name > p2.Name {
return 1
} else if p1.Name < p2.Name {
return -1
}
return 0
})
}
这是一个自定义排序规则的例子,slices.SortFunc接受两个参数,一个是切片,另一个就是比较函数,不考虑复用的话,我们就可以直接传递匿名函数。
闭包
闭包(Clousure)这一概念,在一些语言里也成为lambda表达式,与匿名函数一起使用,闭包就等于函数+环境引用。
package main
import "fmt"
func main() {
grow := Exp(2)
for i := range 10 {
fmt.Printf("2^%d=%d\n", i, grow())
}
}
// 递归求2的幂函数
func Exp(n int) func() int {
e := 1
return func() int {
temp := e
e *= n
return temp
}
}
上述闭包案例和递归很像,但还是有区别的:
- 闭包关注的是函数与其外部作用域的绑定,而不是自身调用。
- 递归关注的是函数调用本身,而不一定涉及外部变量的捕获。
使用闭包计算斐波那契数列
package main
import "fmt"
func main() {
fib := Fib(10)
for n, next := fib(); next; n, next = fib() {
fmt.Println(n)
}
}
func Fib(n int) func() (int, bool) {
a, b, c := 1, 1, 2
i := 0
return func() (int, bool) {
if i >= n {
return 0, false
} else if i < 2 {
f := 1
i++
return f, true
}
a, b = b, c
c = a + b
i++
return a, true
}
}