Closed sailei1 closed 4 years ago
常量 const 名字小写 尽量不要大写,大写代表作用域public const 数值可以作为各种类型使用
枚举
const()
iota 自增值种子
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb)
条件语句
if 在 Go 中条件语句不能有括号, 没有三元运算符 if条件里面可以赋值 注意作用域只在if里面
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")
}
switch 后面可以没有表达式 不用在末位添加break; fallthrough 相当于渗透 必须是最后一个语句
i := 2
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
fallthrough
case 3:
fmt.Println("three")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
panic 中断程序运行
循环 for 条件里面不能有括号 可以省略 初始条件 结束条件 递增表达式 ;占位
省略 初始条件 增值条件 相当于while 循环
都省略 死循环 break跳出
函数 定义 函数名在前 返回类型在后 可以返回多个值 函数体内定义变量 一定要用 否则编译错误 "_" 变量占位 参数没有默认值 函数式编程 函数可以做参数 可以返回函数
func apply(op func(int, int) int, a, b int) int {
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(p).Name()
fmt.Printf("Calling function %s with args "+
"(%d, %d)\n", opName, a, b)
return op(a, b)
}
func main() {
fmt.Println("pow(3, 4) is:", apply(
func(a int, b int) int {
return int(math.Pow(
float64(a), float64(b)))
}, 3, 4))
}
指针 *类型 代表指针 &取地址 不支持指针运算
值传递 值拷贝 外面值不变
func swap(a, b int) {
a,b=b,a
}
a, b := 3, 4
swap(a, b)
fmt.Println("a, b after swap is:", a, b) //3,4
引用传递 引用拷贝 外面值改变
func swap(a, b *int) {
*a,*b=*b,*a
}
a, b := 3, 4
swap(&a, &b)
fmt.Println("a, b after swap is:", a, b) //4,3
go 只有值传递一种 ,要想改变值 必须要传指针进去
数组
数组是值类型 ,[10]int 和[20]int 是不同类型
go 一般不直接使用数组 使用切片
package main
import "fmt"
func editArray(arr *[5]int){
arr[0]=100
for i,v :=range arr{
fmt.Println(i,v)
}
}
func main() {
var arr1 [5]int //有默认值
arr2:=[3]int{1,3,5}
arr3:=[...]int{2,4,6}
grid:=[4][5]int{} // := 需要给个初始值
fmt.Println(arr1,arr2,arr3)
fmt.Println(grid)
for _,v :=range arr3{
fmt.Println(v)
}
fmt.Println("before:",arr1)
editArray(&arr1) //修改数组 需传指针进去
fmt.Println("after:",arr1)
}
Slice
半开半闭区间 包含第一位,不包含最后一位 Slice 本身没数据 对底层array的 一个视图
Reslice 引用的是同一个数组
import "fmt"
func main() {
var s []int //切片默认值nil
arr:=[]int{0,1,2,3,4,5,6,7}
fmt.Println("arr:",arr)
s1:=arr[2:6]
s2:=s1[3:5]
fmt.Println("s1:",s1) //[2,3,4,5]
fmt.Println("s2:",s2) //[5,6]
fmt.Println("arr[:]",arr[:]) //arr[:] [0 1 2 3 4 5 6 7]
// slice 可以向后扩展,不可以向前扩展
//s【i】 不可以超越len(s),向后扩展不可以超越底层数组cap(s)
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n",s1,len(s1),cap(s1))//s1=[2 3 4 5],len(s1)=4,cap(s1)=4
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",s1,len(s2),cap(s2))//s2=[2 3 4 5],len(s2)=2,cap(s2)=2
}
向Slice 添加元素 append() 可以一次添加多个
添加元素时如果超越cap,系统会重新分配更大的底层数组, 原数组拷贝到大数组里,原数组没引用 进行垃圾回收
由于值传递的关系,必须接收append的返回值 s=append(s,v) cap(s) 2倍的扩充
// 切片常规操作
func printSlice(s []int){
fmt.Printf("s=%v,len(s)=%d,cap(s)=%d\n",s,len(s),cap(s))
}
func main() {
s1:=[]int{2,4,6,8}
printSlice(s1) //s=[2 4 6 8],len(s)=4,cap(s)=4
s2:=make([]int ,10,32) //长度为10 容量32
s2[5]=-1
s2=append(s2,100)
printSlice(s2) //s=[0 0 0 0 0 -1 0 0 0 0 100],len(s)=11,cap(s)=32
fmt.Println("copy")
copy(s2,s1) //把s1 复制给s2
printSlice(s2) //s=[2 4 6 8 0 -1 0 0 0 0 100],len(s)=11,cap(s)=32
fmt.Println("deleteByIndex 4")
s2=append(s2[:3],s2[4:]...)
printSlice(s2) //s=[2 4 6 0 -1 0 0 0 0 100],len(s)=10,cap(s)=32
fmt.Println("shift()")
t:=s2[0]
s2=s2[1:]
fmt.Println("first:",t) //2
printSlice(s2) //s=[4 6 0 -1 0 0 0 0 100],len(s)=9,cap(s)=31
fmt.Println("pop()")
f:=s2[len(s2)-1]
s2=s2[:len(s2)-1]
fmt.Println("last:",f) //100
printSlice(s2) //s=[4 6 0 -1 0 0 0 0],len(s)=8,cap(s)=31
}
Map
m:=map[k]{v} // k 无固定顺序 hashmap key 不存在 取 类型默认值 m2:=make(map[string]int) //m2 ==empty map var m3 map[string]int //m3==nil delete(m,key) //删除
func main() {
m:=map[string]string{
"name":"我",
"course":"golang",
}
for k,v :=range m{
fmt.Println(k,v)
}
// map key 注意点
// key 不保证顺序 把key 加到slice里面 循环slice 保证顺序
//map使用哈希表 必须保证相等
//类型 slice map, function 不能判断相等 不能作为key
//自定义结构 包含上述字段 也不能作为key
fmt.Println("Deleting values")
name, ok := m["name"]
fmt.Printf("m[%q] before delete: %q, %v\n",
"name", name, ok)
delete(m, "name")
name, ok = m["name"]
fmt.Printf("m[%q] after delete: %q, %v\n",
"name", name, ok)
String
每个中文 占3个字节(ascii码)
[]rune(s) 每个rune4个字节
func main() {
s:="学习golang!"
fmt.Printf("%v Rune count:%v \n",
s,utf8.RuneCountInString(s)) //学习golang! Rune count:9
bytes := []byte(s)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes)
bytes = bytes[size:]
fmt.Printf("%c ", ch) //学 习 g o l a n g !
}
fmt.Println()
for i, ch := range []rune(s) { //中文转换
fmt.Printf("(%d %c) ", i, ch) //(0 学) (1 习) (2 g) (3 o) (4 l) (5 a) (6 n) (7 g) (8 !)
}
}
常用方法 fields 分割空格 split join //分割 连接 contains,Index 查找 Tolower Toupper 大小写 Trim, TrimRight,Trimleft, 去掉空格
Printf 占位转换
%d 十进制整数
%x, %o, %b 十六进制,八进制,二进制整数。
%f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00
%t 布尔:true或false
%c 字符(rune) (Unicode码点)
%s 字符串
%q 带双引号的字符串"abc"或带单引号的字符'c'
%v 变量的自然形式(natural format)
%T 变量的类型
%% 字面上的百分号标志(无操作数)
struct
//go 只支持封装 不支持继承和多态
//go 没有class 只有 struct
//go 没有构造函数
type treeNode struct {
value int
left,right *treeNode
}
func (node treeNode) print(){
fmt.Printf("%v\n", node.value)
}
//定义方法 (node *treeNode)接收者 只有指针才可以改变结构内容
//nil 指针也可以调用方法
func (node *treeNode) setValue(v int) {
if node !=nil {
node.value = v
}else{
fmt.Println("node is nil")
}
}
// 指针接收者
// 要改变内容必须使用指针接受者
// 结构过大也可以考虑使用指针接收者 (值接收者是拷贝 结构过大会有性能问题)
// 一致性 如有指针接收者,最好都是指针接收者
func (node *treeNode) traverse(){
if node ==nil{
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
//go 局部变量地址 可以直接给外面用
func createNode(v int) *treeNode{
return &treeNode{value:v}
}
func main() {
//var root Node
root :=treeNode{value:3}
root.left=&treeNode{}
root.right=&treeNode{5,nil,nil}
root.right.left=new(treeNode)
root.left.right=createNode(2) //go 指针 可以直接用.访问成员
//fmt.Println(root)
root.setValue(100)
root.traverse()
//root.print()
// nodes:=[]treeNode{
// {value:3},
// {},
// {6,nil,&root},
// }
//fmt.Println(nodes)
//pRoot :=&root
//pRoot.setValue(1000)
//pRoot.print()
//root.print()
// var pRoot *treeNode
//pRoot.setValue(200)
//pRoot =&root
//pRoot.setValue(1200)
//pRoot.print()
}
包和封装
名字一般驼峰命名规则
针对包来说 首字母大写 public 首字母小写 private 适用于 常量 方法 结构 等定义
每个目录只能有一个包
为结构定义的方法必须放在同一个包内,可以不同文件
本地导包 项目需放在 $GOPATH/src 目录下
扩展
package main
import "fmt"
//使用别名
type Queue []int
func (q *Queue)Push(v int){
*q=append(*q,v)
}
func (q *Queue)Pop() int{
head :=(*q)[0]
*q=(*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool{
return len(*q)==0
}
func main() {
q:=Queue{1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
}
gopath 目录结构
每个目录下 只能有一个package main
go build go install ./... 安装 go run 直接编译运行
接口
go 用接口 完成其他语言的继承跟多态
接口由使用者定义 跟其他语言不太一样
一个对象只要全部实现了接口中的方法,那么就实现了这个接口。Go语言中不同的类型可以实现同一个接口
// 使用者
package main
import (
"fmt"
"projects/golearn/retriever/mock"
)
type Retriever interface { //接口由使用者定义
Get(url string) string
}
func download(r Retriever) string {
return r.Get("www.baidu.com")
}
func main() {
r := mock.R{Contents: "xxxx"}
fmt.Println(download(r))
}
// 实现者
package mock
type R struct {
Contents string
}
func (r R) Get(url string) string { //接口实现 只要方法名跟接口定义一样就行
return r.Contents
}
接口的值类型 接口变量 包含 实现者的类型 跟 实现者的值 (实现者的指针指向实现者) 接口变量 自带指针 接口变量 同样采用值传递,几乎不需要使用接口的指针 指针接收者实现只能以指针方式使用;值接收者两者都可
接口类型判断 x.(T) x:表示类型为interface{}的变量 T:表示断言x可能是的类型 interface{} 表示任何类型
接口组合 类似于继承
// 使用者
package main
import (
"fmt"
"projects/golearn/retriever/mock"
)
const url = "https://m.baidu.com"
type Retriever interface { //接口由使用者定义
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string)
}
type RetrieverPoster interface {
Retriever
Poster
TestMethod(s string)
}
func session(s RetrieverPoster) string {
s.Post(url, map[string]string{
"contents": "xxxx ffff",
})
return s.Get(url)
}
func main() {
r := &mock.R{}
fmt.Println(session(r))
r.TestMethod("123abc")
}
// 实现者
package mock
import "fmt"
type R struct {
Contents string
}
func (r *R) Get(url string) string { //接口实现 只要方法名跟接口定义一样就行
fmt.Println("Get")
return r.Contents
}
func (r *R) Post(url string, form map[string]string) {
fmt.Println("Post")
r.Contents = form["contents"]
return
}
func (r *R) TestMethod(s string) {
fmt.Println("TestMethod: ", s)
}
函数式编程
参数 变量 返回值 都可以是函数 闭包 跟JS 差不多
package main
import "fmt"
func adder(sum int) func(int) int {
return func(v int) int { //闭包
sum += v
return sum
}
}
type iAdder func(int) (int, iAdder)
func adder2(base int) iAdder {
return func(i int) (int, iAdder) {
return base + i, adder2(base + i)
}
}
func main() {
s, t := adder(0), 0
for i := 0; i < 10; i++ {
t = s(i)
}
//a, t := adder2(0), 0
//for i := 0; i < 10; i++ {
// t, a = a(i)
//
//}
fmt.Println(t)
}
//为函数实现接口
package main
import (
"bufio"
"fmt"
"io"
"strings"
)
//为函数实现接口
type intGen func() int
func Fib() intGen {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func (g intGen) Read(p []byte) (n int, err error) {
next := g()
if next > 1000 {
return 0, io.EOF
}
s := fmt.Sprintf("%d\n", next) //int 转string
return strings.NewReader(s).Read(p)
}
func printFileContents(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
f := Fib() //
printFileContents(f)
}
defer
如果是列表有个运行栈 先进后出 遇到return panic 还是会执行 确保调用在函数结束时发生 参数在defer 语句时计算
一般常用于 打开 怕你忘记关闭的逻辑 相当于 JS setTimeout(()=>{},0)
package main
import (
"bufio"
"fmt"
"os"
"projects/golearn/functional/fib"
)
func tryDefer() {
for i := 0; i < 100; i++ {
defer fmt.Println(i) // 会记住当前变量的值
if i == 30 {
panic("print too many")
}
}
}
func writeFile(fileName string) {
file, err := os.Create(fileName)
if err != nil {
panic(err)
}
defer file.Close() //最后执行
writer := bufio.NewWriter(file)
defer writer.Flush() //先执行
f := fib.Fibonacci()
for i := 0; i < 20; i++ {
fmt.Fprintln(writer, f())
}
}
func main() {
//tryDefer()
writeFile("fib.txt")
}
panic (尽量少用) 停止当前函数执行 一直向上返回,执行每一层的defer 如果没有遇见recover 程序退出
recover 仅在defer调用中使用 获取panic的值 如果无法处理,可重新panic
package main
import "fmt"
func tryRecover() {
defer func() {
r := recover()
if err, ok := r.(error); ok {
fmt.Println("Error occurred", err)
} else {
panic(fmt.Sprintf("sai's panic: %s", r))
}
}()
b := 0
a := 5 / b
fmt.Println(a)
//panic(123)
}
func main() {
tryRecover()
}
//handler.go
package filelist
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
type userError string
//实现 userError 接口
func (e userError) Error() string {
//return e.Error() // 调用原生报错
return e.Message()
}
func (e userError) Message() string { //实现 外部userError接口 Message()
return string(e)
}
const prefix = "/list/"
func Handler(writer http.ResponseWriter, request *http.Request) error {
if strings.Index(request.URL.Path, prefix) != 0 {
return userError(fmt.Sprintf("path %s must start "+
"with %s",
request.URL.Path, prefix))
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
if err != nil {
//panic(err)
//http.Error(writer, err.Error(), http.StatusInternalServerError)
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
\\web.go
package main
import (
"log"
"net/http"
"os"
"projects/golearn/errhanding/filelistserver/filelist"
)
//定义处理函数 appHandler 返回error
type appHandler func(writer http.ResponseWriter, request *http.Request) error
type userError interface {
error
Message() string //自定义输出报错消息方法
}
//appHandler 当参数传过去 捕捉 处理error
func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
err := handler(writer, request)
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
if err != nil {
code := http.StatusOK
log.Printf("Error handling request: %s", err)
if userErr, ok := err.(userError); ok {
http.Error(writer, userErr.Error(), http.StatusBadRequest)
return
}
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
//fallthrough
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer, http.StatusText(code), code)
}
}
}
func main() {
http.HandleFunc("/", errWrapper(filelist.Handler)) //调用
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
goroutine
每一个并发的执行单元叫作一个goroutine
go run -race *.go 检测数据访问冲突
调度器运行协程 在合适的点进行切换 程序不能完全控制
goroutine 可能的切换点( I/O, select 切换 、channel 、等待锁、runtime.Gosched() 、函数调用)
channel goroutine 双向交互的通道 channel 单向操作会造成死锁 并发基于csp模型 (https://www.jianshu.com/p/36e246c6153d)
package main
import (
"fmt"
"time"
)
func worker(id int, c chan int) {
for n := range c {
fmt.Printf("worker %d received %c\n", id, n)
}
}
func createWorker(id int) chan<- int { //通道取值
c := make(chan int)
go worker(id, c)
return c
}
func chanDemo() {
var channels [10]chan<- int
for i := 0; i < 10; i++ {
channels[i] = createWorker(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i //通道存值 阻塞式 必须要有人收
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i //
}
time.Sleep(time.Millisecond) //等待 确保函数并发执行完 否则main函数运行完会关闭
}
func bufferedChannel() {
c := make(chan int, 3)
go worker(20, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
time.Sleep(time.Millisecond)
}
func channelClose() {
c := make(chan int)
go worker(30, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
close(c) //关闭通道 发送方发起
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
bufferedChannel()
channelClose()
}
package main
import (
"fmt"
)
func doWork(id int, c chan int, done chan bool) { //不要通过共享内存来通信;通过通信来共享内存
for n := range c {
fmt.Printf("worker %d received %c\n", id, n)
go func() {
done <- true
}()
}
}
type worker struct {
in chan int //struct 没有,号分割
done chan bool
}
func createWorker(id int) worker { //通道取值
worker := worker{
in: make(chan int),
done: make(chan bool),
}
go doWork(id, worker.in, worker.done)
return worker
}
func chanDemo() {
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i)
}
for i, worker := range workers {
worker.in <- 'a' + i //通道存值 (阻塞式 必须要有取的操作)
}
for i, worker := range workers {
worker.in <- 'A' + i //
}
for _, worker := range workers {
<-worker.done
<-worker.done
}
}
func main() {
chanDemo()
}
package main
import (
"fmt"
"sync"
)
func doWork(id int, w worker) {
for n := range w.in {
fmt.Printf("worker %d received %c\n", id, n)
//go func() {
// done <- true
//}()
w.done()
}
}
type worker struct {
in chan int //struct 没有,号分割
//done chan bool
done func()
}
func createWorker(id int, wg *sync.WaitGroup) worker { //通道取值
worker := worker{
in: make(chan int),
done: func() {
wg.Done()
},
}
go doWork(id, worker)
return worker
}
func chanDemo() {
var workers [10]worker
var wg sync.WaitGroup //
for i := 0; i < 10; i++ {
workers[i] = createWorker(i, &wg)
}
wg.Add(20)
for i, worker := range workers {
worker.in <- 'a' + i //通道存值 (阻塞式 必须要有取的操作)
}
for i, worker := range workers {
worker.in <- 'A' + i //
}
wg.Wait()
}
func main() {
chanDemo()
}
select channel 不确定谁先到的时候 用select select 使用defalut 会变成非阻塞式获取 select 中使用Nil channel 会被阻塞到 永远不会执行
package main
import (
"fmt"
"math/rand"
"time"
)
func generator() chan int {
out := make(chan int)
go func() {
i := 1
for {
time.Sleep(
time.Duration(rand.Intn(1500)) *
time.Millisecond)
out <- i
i++
}
}()
return out
}
func worker(id int, c chan int) {
for n := range c {
time.Sleep(time.Second)
fmt.Printf("Worker %d received %d\n",
id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func main() {
var c1, c2 = generator(), generator()
var worker = createWorker(-1)
//var w chan int // nil channel select{ case w<-n: 会被一直阻塞 永远不会执行 }
var values []int
tm := time.After(10 * time.Second) //定时器
tick := time.Tick(time.Second) //每秒执行一次
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
select {
case n := <-c1:
values = append(values, n)
case n := <-c2:
values = append(values, n)
case activeWorker <- activeValue:
values = values[1:]
case <-time.After(500 * time.Millisecond): //两次执行相邻的时间差
fmt.Println("timeout")
case <-tick:
fmt.Println(
"queue len =", len(values))
case <-tm:
fmt.Println("bye")
return
}
}
}
传统同步机制
package main
import (
"fmt"
"sync"
"time"
)
type atomicInt struct {
value int
lock sync.Mutex
}
func (a *atomicInt) increment() {
fmt.Println("safe increment")
func() {
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}()
}
func (a *atomicInt) get() int {
a.lock.Lock()
defer a.lock.Unlock()
return a.value
}
func main() {
var a atomicInt
a.increment()
go func() {
a.increment()
}()
time.Sleep(time.Millisecond)
fmt.Println(a.get())
}
迷宫的广度优先搜索 maze.ini
6 5
0 1 0 0 0
0 0 0 1 0
0 1 0 1 0
1 1 1 0 0
0 1 0 0 1
0 1 1 0 0
package main
import (
"fmt"
"os"
)
func readMaze(fileName string) [][]int {
file, err := os.Open(fileName)
if err != nil {
panic(err)
}
var row, col int
fmt.Fscanf(file, "%d %d", &row, &col)
maze := make([][]int, row)
for i := range maze {
maze[i] = make([]int, col)
for j := range maze[i] {
fmt.Fscanf(file, "%d", &maze[i][j])
}
}
return maze
}
type point struct {
i, j int
}
var dirs = [4]point{
{-1, 0}, {0, -1}, {1, 0}, {0, 1}} //上左下右
func (p point) add(r point) point {
return point{p.i + r.i, p.j + r.j}
}
func (p point) at(grid [][]int) (int, bool) {
if p.i < 0 || p.i >= len(grid) {
return 0, false
}
if p.j < 0 || p.j >= len(grid[p.i]) {
return 0, false
}
return grid[p.i][p.j], true
}
func walk(maze [][]int, start, end point) [][]int {
steps := make([][]int, len(maze))
for i := range steps {
steps[i] = make([]int, len(maze[i]))
}
Q := []point{start}
for len(Q) > 0 {
cur := Q[0]
Q = Q[1:]
if cur == end {
break
}
for _, dir := range dirs {
next := cur.add(dir)
val, ok := next.at(maze)
if !ok || val == 1 {
continue //障碍物 跟越界
}
val, ok = next.at(steps)
if !ok || val != 0 {
continue //已经走过
}
if next == start {
continue //返回到起点
}
curStep, _ := cur.at(steps)
steps[next.i][next.j] = curStep + 1 //标记路径
Q = append(Q, next) //把路径添加到队列里去
}
}
return steps
}
func getPath(steps [][]int, end point) ([]point, bool) {
var paths []point
end_value := steps[end.i][end.j]
if end_value == 0 {
//没有到达重点
return paths, false
}
paths = append(paths, end) //将终点先存进去
for {
if end_value <= 0 {
break
}
end_value--
for _, p := range dirs {
next := end.add(p) //寻找周围的点
data, ok := next.at(steps)
if !ok || data != end_value {
continue
}
paths = append(paths, next) //找到下一个点
end = next
}
}
return paths, true
}
func main() {
maze := readMaze("maze/maze.ini")
//for _, row := range maze {
// for _, val := range row {
// fmt.Printf("%3d", val)
// }
// fmt.Println()
//}
steps := walk(maze, point{0, 0},
point{len(maze) - 1, len(maze[0]) - 1})
for _, row := range steps {
for _, val := range row {
fmt.Printf("%3d", val)
}
fmt.Println()
}
path, ok := getPath(steps, point{len(maze) - 1, len(maze[0]) - 1})
if ok {
fmt.Println("最短路径坐标:")
for i := len(path) - 1; i >= 0; i-- {
fmt.Printf("%v ", path[i])
}
} else {
fmt.Println("对不起,没有找到通往终点的路径")
}
}
golang
变量定义
变量名在前 类型在后 类型相同的可以一次多个 后面跟数据类型 声明之后 有默认值 数据类型 可以省略 让编译器 自己推断 := 这种方式声明 作用域在函数体内 不能用于外部声明 var 这种方式 里面外面都可以声明 作用域在包内部 多个 var (aa =3 s="kkk") 多个声明
string 类型需使用双引号
总结 函数内声明 首页 := 包内声明变量 使用 var()
内建变量类型
bool string
(u)int 其中u开头的类型就是无符号整型。 无符号类型能够表示正整数和零。而有符号类型除了能够表示正整数和零外,还可以表示负整数。 uintptr 是个指针 位数默认操作系统
byte 等同于int8,常用来处理ascii字符 rune 等同于int32,常用来处理unicode或utf-8字符
float32 单精度 float64 双精度 小数点后面能跟的小数位数不同
复数 complex 64 32位 实部跟虚部 都是float 32 complex 128 64位 实部跟虚部 都是float 64
类型转换是强制的,没有隐式转换
解决浮点数运算不准确的问题,在运算前我们把参加运算的数先升级(10的X的次方)到整数,等运算完后再降级(0.1的X的次方)