0%

作为一个BanG Dream! It’s MyGO!!!!!的忠实粉丝和Counter-Strike: Global Offensive的忠实玩家,Go语言自然也是必不可少的

本文记录了学习Go官网中的Go by Example的笔记和一些从别的地方的东西

basic

先看一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("go" + "lang")
fmt.Println("1+1 =", 1+1)
fmt.Println("7.0/3.0 =", 7.0/3.0)
fmt.Println(true && false)
fmt.Println(true || false)
fmt.Println(!true)
}

看起来第一行代表使用 package main 声明这个文件属于 main 包

fmt代表输入输出的包

run

使用

1
go run main.go

来编译运行

var

可以使用var 变量名 类型,如

1
var name dingzhen

声明一个

仅在函数内部可以使用:=,如

1
name := "dingzhen"

可以类型推断,也能指定

1
2
var a int = 10  
var b = 20

以及用来枚举的iota //todo

1
2
3
4
5
const (  
Sunday = iota
Monday
Tuesday
)

string

首先, 在fmt中有String()方法,可以用来输出string

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"
//"strconv"
)

type coordinate struct {
d, m, s float64
h rune
}

// 实现 String 方法
func (c coordinate) String() string {
return fmt.Sprintf("%g° %g'%g\" %c", c.d, c.m, c.s, c.h)
}

func main() {
lat := coordinate{d: 4, m: 30, s: 0.0, h: 'N'}
long := coordinate{d: 135, m: 54, s: 0.0, h: 'E'}

fmt.Printf("Elysium Planitia is at %s, %s\n", lat, long)
}

还可以导入strings包来进行string的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"fmt"
s "strings"
)
var p = fmt.Println
func main() {
p("Contains: ", s.Contains("test", "es"))
p("Count: ", s.Count("test", "t"))
p("HasPrefix: ", s.HasPrefix("test", "te"))
p("HasSuffix: ", s.HasSuffix("test", "st"))
p("Index: ", s.Index("test", "e"))
p("Join: ", s.Join([]string{"a", "b"}, "-"))
p("Repeat: ", s.Repeat("a", 5))
p("Replace: ", s.Replace("foo", "o", "0", -1))
p("Replace: ", s.Replace("foo", "o", "0", 1))
p("Split: ", s.Split("a-b-c-d-e", "-"))
p("ToLower: ", s.ToLower("TEST"))
p("ToUpper: ", s.ToUpper("test"))
}

输出为

1
2
3
4
5
6
7
8
9
10
11
12
13
$ go run string-functions.go
Contains: true
Count: 2
HasPrefix: true
HasSuffix: true
Index: 1
Join: a-b
Repeat: aaaaa
Replace: f00
Replace: f0o
Split: [a b c d e]
ToLower: test
ToUpper: TEST

For if/else switch

for有许多写法

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
28
29
30
31
32
33
34
35
36
37
38
package main
import "fmt"
func main() {
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
for j := 0; j < 3; j++ {
fmt.Println(j)
}
for i := range 3 {
fmt.Println("range", i)
}
for {
fmt.Println("loop")
break
}
//
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
//
jk := 2
fmt.Print("Write ", jk, " as ")
switch jk {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
}

反正就和c语言把小括号去了差不多

Arrays

1
2
3
4
5
6
7
var a [5]int

b = [...]int{100, 3: 400, 500}//使用...推断数组长度
twoD = [2][3]int{
{1, 2, 3},
{1, 2, 3},
}

Slices:类似于可变长数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  //创建一个新的切片,并且是空的字符串,然后深拷贝
c := make([]string, len(s))
copy(c, s)
//切片切取,和matlab之类的波哈不多
l = s[:5]
fmt.Println("sl2:", l)


l = s[2:]
fmt.Println("sl3:", l)

// 创建一个3x4的二维切片
twoD := make([][]int, 3)
for i := range twoD {
twoD[i] = make([]int, 4)
}

maps

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//作为集合
func main() {
set := make(map[string]bool)

// 添加元素
set["apple"] = true
set["banana"] = true

// 检查元素是否存在
if set["apple"] {
fmt.Println("Apple is in the set")
}

// 删除元素
delete(set, "banana")

// 遍历集合
for item := range set {
fmt.Println(item)
}
}
//也可以嵌套
func main() {
users := map[string]map[string]string{
"alice": {
"email": "alice@example.com",
"phone": "123-456-7890",
},
"bob": {
"email": "bob@example.com",
"phone": "098-765-4321",
},
}

fmt.Println("Alice's email:", users["alice"]["email"])

// 添加新用户
users["charlie"] = map[string]string{
"email": "charlie@example.com",
"phone": "111-222-3333",
}

// 遍历嵌套 map
for name, info := range users {
fmt.Printf("%s: Email - %s, Phone - %s\n", name, info["email"], info["phone"])
}
}

Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//格式类似于
package main
import "fmt"
func plus(a int, b int) int {
return a + b
}
func plusPlus(a, b, c int) int {
return a + b + c
}
func main() {
res := plus(1, 2)
fmt.Println("1+2 =", res)
res = plusPlus(1, 2, 3)
fmt.Println("1+2+3 =", res)
}

Variadic Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import "fmt"
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
// func sum(nums ...int) 定义了一个名为 sum 的可变参数函数。

// ...int 表示 nums 可以接受任意数量的 int 类型参数。

// _ 用于忽略循环索引,因为我们只需要值。
func main() {
nums := []int{1, 2, 3, 4}
sum(nums...)
}

Closure

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
28
29
30
31
package main
import "fmt"
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
// intSeq 是一个返回函数的函数,也称为高阶函数。
// 它定义了一个局部变量 i,初始值为 0。
// 返回一个匿名函数,这个匿名函数每次调用时会增加 i 并返回新值。
// 这个匿名函数形成了一个闭包,它"捕获"了外部函数 intSeq 的 i 变量。
func main() {
nextInt := intSeq()
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
newInts := intSeq()
fmt.Println(newInts())
}
// nextInt := intSeq() 调用 intSeq 函数,返回一个新的闭包函数,赋值给 nextInt。
// 连续三次调用 nextInt(),每次调用都会增加并返回 i 的值。
// newInts := intSeq() 创建一个新的闭包实例。
// 调用 newInts() 会从 1 开始计数,因为它是一个全新的闭包实例。

// 闭包的工作原理

// 每次调用 intSeq() 都会创建一个新的 i 变量实例。
// 返回的匿名函数可以访问并修改这个 i 变量。
// 即使 intSeq 函数已经返回,i 变量仍然存在于内存中,被闭包引用。

oop

struct

和c的差不多

method

感觉这个写法有点神人

就是与特定类型关联的函数,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import "fmt"

type rect struct {
width, height int
}
func (r *rect) area() int {
return r.width * r.height
}
func (r rect) perim() int {
return 2*r.width + 2*r.height
}
func main() {
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
fmt.Println("perim:", r.perim())
rp := &r
fmt.Println("area: ", rp.area())
fmt.Println("perim:", rp.perim())
}

还是要注意指针和值

1
rp := &r

会在改变rp的时候改变r的值

interface

约定俗成:一般接口名字结尾用er

和别的语言也差不多,这就是go中的多态的实现

关于其他:没有class,对象,继承,面向对象就用method和interface的组合来实现

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
28
29
30
31
32
33
34
35
36
37
38
package main
import (
"fmt"
"math"
)
type geometry interface {
area() float64
perim() float64
}
type rect struct {
width, height float64
}
type circle struct {
radius float64
}
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
func main() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
}

struct 嵌入实现转发

在struct中只给定字段类型,不给定字段名即可

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
28
29
30
package main
import (
"fmt"
)
type report struct {
sol int
temperture//这样temperture的所有字段和方法都可以直接通过report实例访问
location
}
type temperture struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
func (t temperture) average() celsius {
return (t.high + t.low) / 2
}
func main() {
bradbury := location{-4.5895, 137.4417}
t := temperture{high: -1.0, low: -78.0}
report := report{
sol: 15,
temperture: t,
location: bradbury,
}
fmt.Println(report.average())//这样就可以直接访问了
//原本的写法fmt.Println(report.temperture.average())当然也是可以的
}

error

nil

声明为接口的变量未被赋值的时候零值是nil

接口类型的变量只有在类型和值都是nil的时候才为nil

例题:

• 亚瑟被一位骑士挡住了去路。正如 leftHand item 变量的值 nil 所示,这位英雄手上正空无一物。
请实现一个拥有 pickup(i
item) 和 give(to *character) 等方法的character 结构,然后使用你在本节学到的知识编写一个脚本,使得亚瑟可以拿起一件物品并将其交给骑士,与此同时为每个动作打印出适当的描述。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main
import (
"fmt"
)
type item struct {
name string
}
type character struct {
name string
leftHand *item
}
// pickup 方法让角色拿起物品
func (c *character) pickup(i *item) {
if c.leftHand != nil {
fmt.Printf("%s's hand is already occupied.\n", c.name)
return
}
c.leftHand = i
fmt.Printf("%s picks up %s.\n", c.name, i.name)
}
// give 方法让角色将物品交给其他角色
func (c *character) give(to *character) {
if c.leftHand == nil {
fmt.Printf("%s has nothing to give.\n", c.name)
return
}
if to.leftHand != nil {
fmt.Printf("%s cannot accept %s; hand is occupied.\n", to.name, c.leftHand.name)
return
}
fmt.Printf("%s gives %s to %s.\n", c.name, c.leftHand.name, to.name)
to.leftHand = c.leftHand
c.leftHand = nil
}
func main() {
// 创建角色和物品
arthur := &character{name: "Arthur"}
knight := &character{name: "The Knight"}
sword := &item{name: "Excalibur"}
// 执行拾取和交给操作
arthur.pickup(sword)
arthur.give(knight)
}

error

按照惯例函数返回最后一个值应该是表示错误

可以用New来创建一个创建一个新的、简单的error

1
var ErrDivideByZero = errors.New("cannot divide by zero") 

这样后面就可以用

1
2
3
4
5
6
7
8
if err == ErrDivideByZero {  
fmt.Println("Error:", err)
} else {
fmt.Println("Unexpected error:", err)
}
} else {
fmt.Println("Result:", result)
}

自定义错误类型:

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
28
29
30
31
32
33
34
35
package main

import (
"errors"
"fmt"
)

type argError struct {
arg int
message string
}

func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.message)
}

func f(arg int) (int, error) {
if arg == 42 {

return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}

func main() {

_, err := f(42)
var ae *argError
if errors.As(err, &ae) {
fmt.Println(ae.arg)
fmt.Println(ae.message)
} else {
fmt.Println("err doesn't match argError")
}
}

defer

允许推迟函数的执行直到当前函数返回。这在处理资源清理、锁的释放、文件关闭等场景中特别有用

defer语句的执行时机在函数返回之前,即使它位于return语句之后:

多个 defer 语句,它们会以后进先出(LIFO)的顺序执行。

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
28

package main

import "fmt"

func main() {
fmt.Println("Enter main")
defer fmt.Println("Defer in main - 1")

func() {
fmt.Println("Enter anonymous function")
defer fmt.Println("Defer in anonymous function - 1")
defer fmt.Println("Defer in anonymous function - 2")
fmt.Println("Exit anonymous function")
}()

deferOrder()

defer fmt.Println("Defer in main - 2")
fmt.Println("Exit main")
}

func deferOrder() {
defer fmt.Println("Defer in deferOrder - 1")
defer fmt.Println("Defer in deferOrder - 2")
fmt.Println("In deferOrder function")
}

输出为

1
2
3
4
5
6
7
8
9
10
11
Enter main  
Enter anonymous function
Exit anonymous function
Defer in anonymous function - 2
Defer in anonymous function - 1
In deferOrder function
Defer in deferOrder - 2
Defer in deferOrder - 1
Exit main
Defer in main - 2
Defer in main - 1

recover:用来处理panic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"
func mayPanic() {
panic("a problem")
}
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered. Error:\n", r)
}
}()
mayPanic()
fmt.Println("After mayPanic()")
}

输出为

1
2
3
$ go run recover.go
Recovered. Error:
a problem

concurrent

goroutine

Goroutine是Go语言中最基本的并发执行单元。它是一个轻量级的”线程”,由Go运行时管理

创建一个goroutine非常简单,只需要在函数调用前加上go关键字:

1
2
3
4
5
6
7
8
func hello() {  
fmt.Println("Hello from goroutine!")
}

func main() {
go hello()// 启动一个新的goroutine
time.Sleep(1 * time.Second) // 等待goroutine执行
}

Channel

Channel是goroutine之间通信的管道。它可以让一个goroutine发送特定值到另一个goroutine

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
func main() {  
// 创建一个整数类型的通道
ch := make(chan int)

// 在goroutine中发送数据
go func() {
ch <- 42 // 发送数据到通道
}()

value := <-ch // 从通道接收数据
fmt.Println(value)
}

Channel的类型:

  1. 无缓冲通道:

    1
    ch := make(chan int)
    • 发送和接收操作必须同时准备好才能进行
  2. 有缓冲通道:

    1
    ch := make(chan int, 3)
    • 可以存储多个值,直到缓冲区满

同步机制

Go提供了多种同步机制:

WaitGroup

用于等待一组goroutine完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var wg sync.WaitGroup  

func worker(id int) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}

func main() {
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i)
}
wg.Wait()
}

Mutex(互斥锁)

用于保护共享资源:

1
2
3
4
5
6
7
8
9
10
var (  
mutex sync.Mutex
counter int
)

func increment() {
mutex.Lock()
counter++
mutex.Unlock()
}

Select

Select语句可以同时等待多个通道操作:

1
2
3
4
5
6
7
8
select {  
case msg1 := <-ch1:
fmt.Println("Received from ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("Received from ch2:", msg2)
case <-time.After(time.Second):
fmt.Println("Timeout")
}

本文提出了一种新的条件像素合成模型,并在物体计数这个下游任务中有关键优势

我们提出了一种像素合成模型,它学习了一个有条件的2D空间坐标网格以及连续的时间维度,这对遥感应用很合适,因为同一位置可以由不同设备(如NAIP或Sentinel-2)在不同时间(如2016年或2018年)捕获。

problem set

输入:我们给定了同一位置的高分辨率(HR)和低分辨率(LR)两个时间序列。直观地说,我们希望利用HR图像中丰富的信息和LR图像的高时间频率,以获得两者的最佳效果

输出:某时刻t的HR

image-20240505213411660

method

  1. 图像特征映射器(Image Feature Mapper)

    首先对低分辨率图像进行最近邻重采样,得到拼接后的输入图像 I_cat(一层HR,一层LR,2C x H x W)

    使用邻域编码器 F_E 和全局编码器 F_A 提取图像特征,得到特征图 I_fea(C_fea x H x W)

  2. 位置编码器(Positional Encoder)

    使用傅里叶特征和空间坐标嵌入,得到位置编码 E(x,y,t)

  3. 像素合成器(Pixel Synthesizer)

    根据 I_fea、E(x,y,t) 和随机向量 z,独立地合成每个像素值

    具体来说,G_p 包含以下几个关键组件:

    1. 映射函数 g_z:
      • 它将位置编码 E(x,y,t) 映射到一个 C_fea 维的向量。
      • 这个映射函数帮助 G_p 学习如何利用位置信息来生成对应位置的像素值。
    2. 样式注入模块:
      • 首先使用一个多层感知机 M 将噪声向量 z 映射到一个样式向量 w。
      • 然后在 G_p 的多个全连接层中注入这个样式向量 w,以保持生成像素之间的样式一致性。
    3. 逐像素生成:
      • 对于每个坐标(x,y,t),G_p 根据 I_fea(x,y)、g_z(E(x,y,t)) 和 w 独立地生成对应的像素值。
      • 这种逐像素、条件独立的生成方式使得 G_p 可以并行高效地生成整张高分辨率图像。

    使用多层感知机和样式注入的方式生成最终的高分辨率图像

  4. 对抗训练

    引入判别器 D 进行对抗训练,以提高生成图像的质量

Today, as I delved into a paper on dense shape matching and downloaded the dataset, I found myself interested in how these datasets pertaining to shape matching store correspondences between pairs of shapes. Namely, I am curious about how they preserve the ground truth of these correspondences. Then I chose SMAL dataset to conduct my research on this issue. It’s a dataset containing information on animals.

Read more »