最近文章

Go语言:标识符命名最佳实践

在 Go 语言中,标识符的命名非常重要,它们使你的代码易于阅读、易于理解和易于维护。编写Go代码时,遵循以下标识符命名的最佳实践:1、使用有意义的名称使用能够准确反映其作用的名称,让代码易于理解和维护。func calculateArea(width float64, height float64) float64 { return width * height } 2、避免使用缩写除非使用缩
标签:

Go语言获取对象的类型

Go reflect包含有检查变量类型的方法,示例代码如下:package main import ( "fmt" "reflect" ) func main() { v1:= "string" v2 := 100 v3 := 11.1 fmt.Println(reflect.TypeOf(v1)) fmt.Println(refle
标签:

Go语言if语句判断map key是否存在的简洁方式

直接上示例,简洁的写法:if val, exists:= map["key"]; exists{ //此处为key存在的业务代码 }Map在Go语言里,获取Map某个key的值,map['key']会返回key对应的值和key是否存在的布尔值。var val int var exists bool val, exists = map["key"]If在Go语言里,if语句是可以包含条件语句
标签:

Go语言:变量的作用域(本地和全局变量)

变量声明的位置Go语言变量声明的位置有三种级别:包(package)级别:变量在函数外声明函数级别:变量在函数内,包括函数的传参参数变量,但在代码块外声明块级别:这个分两种,一种是显式,会使用大括号{}括起来的代码;另一种是隐式,如for,if后的初始化表达式。变量位置示例package main import "fmt" var test = "test" //在函数外,包级别变量 fu
标签:

Mac终端(zsh)使用SS代理进行连接

平时我们在使用SS代理服务后,浏览器可以正常fan墙使用,但是在终端里面使用git/brew/pip等命令时,速度却是出奇的慢,实际上这是由于虽然SS设置了全局设置,但是并没有把全局设置传递到终端。我们需要一些设置使得终端也能正确使用SS代理服务。SS搭建问题在这里不进行展开叙述,网上教程也很多,也有现成的SS服务可以购买。下面对终端设置SS代理服务进行简单的阐述。S:shadowsocks2.检
标签:

Go语言日志框架logrus封装

logrus在github上是一个star数比较高的go日志框架,简单封装如下:package log import ( "fmt" "github.com/Sirupsen/logrus" "runtime" "strings" ) var logger = logrus.New() // 封装logrus.Fields type Fields logrus.Fields func SetLog
标签:

Go语言逐行读取文件

使用Go语言逐行读取文件,最简单的方法是使用 bufio.Scanner。常规做法简单示例:package main import ( "bufio" "fmt" "log" "os" ) func main() { file, err := os.Open("/path/to/file.txt") if err != nil {
标签:

Go语言基本类型:整型

整型分为有符号(signed)或无符号(unsigned)。有符号整型有符号整型根据整型所占的空间分为:类型占用的空间取值范围int依赖平台依赖平台int88 bit/1字节-2^7 至 2^7 -1int1616 bit/2两字节-2^15 至 2^15 -1int3232 bit/4字节-2^31 至 2^31 -1int6464 bit/8字节-2^6
标签:

Go日志框架logrus的基本用法

以package级别方式使用logruspackage main import ( "os" log "github.com/sirupsen/logrus" ) func init() { // 设置日志格式 log.SetFormatter(&log.JSONFormatter{}) // 设置输出 log.SetOutput(os.Stdout) // 设
标签:

Go语言:变量的声明

在计算机语言中,变量是以可读的名称表示保存值的存储位置。变量存储的值都有对应的类型,它的类型决定了变量存储值的空间以及操作。在Go语言中,它的命名需要符合Go语言:标识符命名规则。变量在使用之前,需要先声明变量。Go语言提供了多种声明变量的方式,主要分为:使用关键词var基本格式声明变量多变量的声明使用简短赋值符:=声明变量下面介绍变量的多种声明方式声明变量的基本格式GO语言声明变量的基本格式是使
标签:

Go语言:标识符命名规则

标识符实际是名称的另一种叫法,它包括了变量名、函数名、方法名、类型名、包名等。Go语言规定了有下面几点命名的语法要求。 首字符必须是unicode字母或者下划线(_)先要说明,unicode字母并非只是英文字母表里的26个字母,汉字也是属于Unicode字母。对Unicode字母感兴趣的可以参考:The Unicode Standard 8.0, Section 4.5。换句话说,除了下
标签:

Go语言把map接口转换为google.protobuf.Struct

以下是go语言把map[string]interface{}转换为google.protobuf.Struct:package pbimport ( "fmt" "reflect" st "github.com/golang/protobuf/ptypes/struct")// ToStruct转换map[string]interface{} 为ptypes.Structfunc ToStruct
标签:

GPU架构学习资源

这里收藏了gpu学习的网站,pdf等等资源:http://courses.cms.caltech.edu/cs179/http://www.amd.com/Documents/GCN_Architecture_whitepaper.pdfhttps://community.arm.com/graphics/b/bloghttp://cdn.imgtec.com/sdk-documentation/
标签:

Go语言:标识符命名最佳实践

在 Go 语言中,标识符的命名非常重要,它们使你的代码易于阅读、易于理解和易于维护。

编写Go代码时,遵循以下标识符命名的最佳实践:

1、使用有意义的名称

使用能够准确反映其作用的名称,让代码易于理解和维护。

func calculateArea(width float64, height float64) float64 {
  return width * height
}

2、避免使用缩写

除非使用缩写比使用全称更易于理解,否则应该避免使用缩写。

func convertMillisecondsToSeconds(milliseconds int64) float64 {
  return float64(milliseconds) / 1000
}

3、使用驼峰式命名

Go语言中建议使用驼峰式命名(CamelCase),即首字母小写,后续单词首字母大写。

var backgroundColor string
var userName string

4、对于公共变量或函数,使用首字母大写的驼峰式命名

这样可以使其在包外可见,符合Go语言的导出规则。

type Rectangle struct {
  Width  float64
  Height float64
}

func NewRectangle(width float64, height float64) *Rectangle {
  return &Rectangle{Width: width, Height: height}
}

5、对于私有变量或函数,使用首字母小写的驼峰式命名

这样可以使其在包外不可见,符合Go语言的封装规则。

type Rectangle struct {
  Width  float64
  Height float64
}

func NewRectangle(width float64, height float64) *Rectangle {
  return &Rectangle{Width: width, Height: height}
}

6、避免使用下划线

Go语言中通常不使用下划线来分隔单词,除非是用于声明未使用的变量。

var fullName string
var firstName string

7、避免使用关键字

Go语言中有一些关键字(如if、for、func等),不能用作标识符。

 

Go语言获取对象的类型

更新于 2023.02.28 1分钟阅读 0 评论 5 推荐

    Go语言使用

    作者: mayon
  1. Go语言获取对象的类型 Page 1
  2. Go语言逐行读取文件 Page 3
  3. Go语言if语句判断map key是否存在的简洁方式 Page 7

Go reflect包含有检查变量类型的方法,示例代码如下:

package main

import (
    "fmt"
    "reflect"
)

func main() {

    v1:= "string"
    v2 := 100
    v3 := 11.1

    fmt.Println(reflect.TypeOf(v1))
    fmt.Println(reflect.TypeOf(v2 ))
    fmt.Println(reflect.TypeOf(v2 ))
}

依次输出如下:

string
int
float64

参考文档:http://golang.org/pkg/reflect/#Type

Go语言if语句判断map key是否存在的简洁方式

更新于 2023.02.10 1分钟阅读 0 评论 5 推荐

    Go语言使用

    作者: mayon
  1. Go语言获取对象的类型 Page 1
  2. Go语言逐行读取文件 Page 3
  3. Go语言if语句判断map key是否存在的简洁方式 Page 7

直接上示例,简洁的写法:

if val, exists:= map["key"]; exists{
    //此处为key存在的业务代码
}

Map

在Go语言里,获取Map某个key的值,map['key']会返回key对应的值和key是否存在的布尔值。

var val int
var exists bool
val, exists = map["key"]

If

在Go语言里,if语句是可以包含条件语句和初始化语句。在上面示例中即同时使用了这两种语句的声明:

val,exists: dict['key']:此语句为初始化语句,使用map['key']返回值对val和exists做初始化。

分号后的exists:此为条件语句,判断map里是否存在指定的“key”。

总结

在Go语言里,结合Map和If语句的语法特性,就可以很简单在一个语句里完成map的key是否存在的判断。

需要注意的是,示例中的val,exists是局部变量,它们的作用域是if代码块级。

 

Go语言:变量的作用域(本地和全局变量)

变量声明的位置

Go语言变量声明的位置有三种级别:

  • 包(package)级别:变量在函数外声明
  • 函数级别:变量在函数内,包括函数的传参参数变量,但在代码块外声明
  • 块级别:这个分两种,一种是显式,会使用大括号{}括起来的代码;另一种是隐式,如for,if后的初始化表达式。

变量位置示例

package main

import "fmt"

var test = "test"  //在函数外,包级别变量

func main() {
	a, b := 1, 2  //a和b,c在main函数内定义,函数级别变量
	c := sum(a, b)
	fmt.Println(c)
	fmt.Println(test)

    
	for i := 0; i < 10; i++ {   //i为for循环内定义,为隐式块级别变量
		var d = i + 1    //d在for的循环体内,以大括号{}括起来,为显式块级别变量
		fmt.Println(d)
	}

}

func sum(a, b int) int {   //函数参数a和b,在函数内定义,为函数级别变量
	fmt.Println(a)
	fmt.Println(b)
	nu := a + b
	return nu
}

变量作用域

变量声明的位置决定了变量允许在什么地方被访问以及变量的声明周期。变量的可访问范围称为变量的作用域。Go语言中,变量的作用域分为两类:

  • 局部变量,也称为本地变量
  • 全局变量

局部变量

在函数级别和块级别内声明的变量为局部变量。局部变量有以下特征:

  • 局部变量只能在它们的块或函数中访问。
  • 局部变量的生命周期只到块或声明它们的函数的末尾。之后,它们被垃圾回收器回收。
  • 已经声明的局部变量,不能在同一块或函数中重新声明。

示例:

package main

import "fmt"

func main() {
    var a= "astrng"
    fmt.Println(a)
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
    fmt.Println(i)
}

func testLocal() {
    fmt.Println(a)
}

这个代码编译时会报错:

undefined: i
undefined: a

原因:

i:为for循环体内的块级别的局部变量,在定义它的for循环体外访问,就会报undefined。

a:为main函数级别的局部变量,在testLocal函数内无法访问,会报undefined错误。

全局变量

在包级别声明的变量为全局变量。全局变量有以下特征:

  • 全局变量是以小写开头,此全局变量只能在定义它的包(package)内使用。即变量在包内可见。
  • 全局变量是以大写开头,此全局变量可以跨不同包(package)之间使用。即变量在所有地方可见。
  • 全局变量在程序的整个生命周期中都可用。
  • 不能使用:=运算符声明全局变量。
package main

import "fmt"

var a = "astring"

func main() {
    testGlobal()
}

func testGlobal() {
    fmt.Println(a)  //运行结果是,正确打印出a的值“astring”
}

局部变量和全局变量同名

Go语言是运行局部变量和全局变量同名,同名的变量在局部作用域范围,它将遮蔽外部作用域中的变量。

示例:

package main

import "fmt"

var a = "globala"

func main() {
    var a = "locala"
    fmt.Println(a)  //此处的a会遮蔽外面定义的a全局变量,输出locala
}
func testGlobal() {
  fmt.Println(a)  //此处是全局变量a,输出globala
}

 

 

Mac终端(zsh)使用SS代理进行连接

平时我们在使用SS代理服务后,浏览器可以正常fan墙使用,但是在终端里面使用git/brew/pip等命令时,速度却是出奇的慢,实际上这是由于虽然SS设置了全局设置,但是并没有把全局设置传递到终端。我们需要一些设置使得终端也能正确使用SS代理服务。
SS搭建问题在这里不进行展开叙述,网上教程也很多,也有现成的SS服务可以购买。
下面对终端设置SS代理服务进行简单的阐述。

S:shadowsocks

2.检查代理状态

curl https://ip.cn
curl curl ipinfo.io #备用查询接口

3.打开SS设置

获取设置内容高级项中,SS所监听的端听IP以及监听端口

4.编辑zsh配置文件

vim ~/.zshrc 

使用socks5协议对刚才获取到的监听IP与监听端口进行拼接
socks5://(IP):(端口)
添加以下内容到配置文件内 (使用刚才拼接好的地址)
alias proxy=’export all_proxy=socks5://127.0.0.1:1080’#用于开启终端代理
alias unproxy=’unset all_proxy’#用于关闭终端代理
保存退出后
source ~/.zshrc

5.应用配置

proxy #开启终端代理
curl https://ip.cn #发现此时ip地址发生了变化,说明终端代理设置成功

注意:不能使用ping检测是否成功代理:
ss基于的SOCKS 5代理协议,支持TCP和UDP服务。而ping是基于icmp协议的,因此在ss下不能使用ping作为检测标准。

6.关闭终端代理

在使用国内镜像源的时候,需要把终端代理关闭,要不然无法进行更新与下载
unproxy #关闭终端代理

Go语言日志框架logrus封装

logrus在github上是一个star数比较高的go日志框架,简单封装如下:

package log
import (
"fmt"
"github.com/Sirupsen/logrus"
"runtime"
"strings"
)
var logger = logrus.New()
// 封装logrus.Fields
type Fields logrus.Fields
func SetLogLevel(level logrus.Level) {
logger.Level = level
}
func SetLogFormatter(formatter logrus.Formatter) {
logger.Formatter = formatter
}
// Debug
func Debug(args ...interface{}) {
if logger.Level >= logrus.DebugLevel {
entry := logger.WithFields(logrus.Fields{})
entry.Data["file"] = fileInfo(2)
entry.Debug(args)
}
}
// 带有field的Debug
func DebugWithFields(l interface{}, f Fields) {
if logger.Level >= logrus.DebugLevel {
entry := logger.WithFields(logrus.Fields(f))
entry.Data["file"] = fileInfo(2)
entry.Debug(l)
}
}
// Info
func Info(args ...interface{}) {
if logger.Level >= logrus.InfoLevel {
entry := logger.WithFields(logrus.Fields{})
entry.Data["file"] = fileInfo(2)
entry.Info(args...)
}
}
// 带有field的Info
func InfoWithFields(l interface{}, f Fields) {
if logger.Level >= logrus.InfoLevel {
entry := logger.WithFields(logrus.Fields(f))
entry.Data["file"] = fileInfo(2)
entry.Info(l)
}
}
// Warn
func Warn(args ...interface{}) {
if logger.Level >= logrus.WarnLevel {
entry := logger.WithFields(logrus.Fields{})
entry.Data["file"] = fileInfo(2)
entry.Warn(args...)
}
}
// 带有Field的Warn
func WarnWithFields(l interface{}, f Fields) {
if logger.Level >= logrus.WarnLevel {
entry := logger.WithFields(logrus.Fields(f))
entry.Data["file"] = fileInfo(2)
entry.Warn(l)
}
}
// Error
func Error(args ...interface{}) {
if logger.Level >= logrus.ErrorLevel {
entry := logger.WithFields(logrus.Fields{})
entry.Data["file"] = fileInfo(2)
entry.Error(args...)
}
}
// 带有Fields的Error
func ErrorWithFields(l interface{}, f Fields) {
if logger.Level >= logrus.ErrorLevel {
entry := logger.WithFields(logrus.Fields(f))
entry.Data["file"] = fileInfo(2)
entry.Error(l)
}
}
// Fatal
func Fatal(args ...interface{}) {
if logger.Level >= logrus.FatalLevel {
entry := logger.WithFields(logrus.Fields{})
entry.Data["file"] = fileInfo(2)
entry.Fatal(args...)
}
}
// 带有Field的Fatal
func FatalWithFields(l interface{}, f Fields) {
if logger.Level >= logrus.FatalLevel {
entry := logger.WithFields(logrus.Fields(f))
entry.Data["file"] = fileInfo(2)
entry.Fatal(l)
}
}
// Panic
func Panic(args ...interface{}) {
if logger.Level >= logrus.PanicLevel {
entry := logger.WithFields(logrus.Fields{})
entry.Data["file"] = fileInfo(2)
entry.Panic(args...)
}
}
// 带有Field的Panic
func PanicWithFields(l interface{}, f Fields) {
if logger.Level >= logrus.PanicLevel {
entry := logger.WithFields(logrus.Fields(f))
entry.Data["file"] = fileInfo(2)
entry.Panic(l)
}
}
func fileInfo(skip int) string {
_, file, line, ok := runtime.Caller(skip)
if !ok {
file = "<???>"
line = 1
} else {
slash := strings.LastIndex(file, "/")
if slash >= 0 {
file = file[slash+1:]
}
}
return fmt.Sprintf("%s:%d", file, line)
}

 

Go语言逐行读取文件

更新于 2023.03.13 9分钟阅读 0 评论 5 推荐

    Go语言使用

    作者: mayon
  1. Go语言获取对象的类型 Page 1
  2. Go语言逐行读取文件 Page 3
  3. Go语言if语句判断map key是否存在的简洁方式 Page 7

使用Go语言逐行读取文件,最简单的方法是使用 bufio.Scanner

常规做法

简单示例:

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("/path/to/file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // 注意,如果读取的行长度超过64k,则会报错
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

字符串长度超出64K

Scan在行超过 65536 个字符时出错。如果知道行长度大于 64K,可以使用 Buffer() 方法来增加Scan的容量。

添加Buffer的示例:

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("/path/to/file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)

    const maxCapacity = longLineLen  // 需要的容量
    buf := make([]byte, maxCapacity)
    scanner.Buffer(buf, maxCapacity)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

使用Buffer核心代码是:

buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)

Go语言基本类型:整型

整型分为有符号(signed)或无符号(unsigned)。

有符号整型

有符号整型根据整型所占的空间分为:

类型占用的空间取值范围
int依赖平台依赖平台
int88 bit/1字节-2^7 至 2^7 -1
int1616 bit/2两字节-2^15 至 2^15 -1
int3232 bit/4字节-2^31 至 2^31 -1
int6464 bit/8字节-2^63 至26^3 - 1

int类型所占空间依赖于操作系统,主要分为两种:

  • 在32位的机器上,int占用的空间为32位,即是4字节。
  • 在64位的机器上,int占用的空间位64位,即是8字节。

声明int类型的变量

var i int

声明变量为int时,如果没有显式赋值,其默认值为0。

隐式声明int变量

i := 1
var j = 1

使用整型数值做简短变量声明或者类型推断声明,变量的默认类型时int。可以使用reflect.TypeOf()查看变量的类型:

package main
import (
    "fmt"
    "reflect"
)
func main() {
    i := 2
    fmt.Printf("i的类型时%s\n", reflect.TypeOf(i))
}

查看int类型所占空间

go语言的bits.UintSize和unsafe.Sizeof()都可以用来获取int类型所占用bit大小。

package main

import (
    "fmt"
    "math/bits"
    "unsafe"
)

func main() {
    sizeOfIntInBits := bits.UintSize
    fmt.Printf("%d bit\n", sizeOfIntInBits)
    
    var i int
    fmt.Printf("%d 字节\n", unsafe.Sizeof(i))
}

使用场景

在大多数需要有符号的整型的地方,推荐使用int。以下情况则使用其他类型的整型:

  1. 在32位的机器上,数值超过-2^31到2^31范围时,使用整型int64。在32位机器上,int64是有两个32位的地址构成的。
  2. 数值范围较小时,使用适合的整型类型,如人的年龄,可以使用int8。这种情况下,遵守保小不保大的原则。
  3. 只有在比较大的整型数值才会使用int64类型。例如:time.Duration是int64。

无符号整型

对应于有符号整型,无符号整型分为:

类型占用空间取值范围
uint依赖平台依赖平台
uint88 bit/1字节0 至 2^8 -1
uint1616  bit/2字节0 至 2^16 -1
uint3232  bit/4字节0 至 2^32 -1
uint6464  bit/8字节0 至 2^64 -1
uintptr依赖平台依赖平台

 

uint类型所占空间依赖于操作系统,主要分为两种:

  • 在32位的机器上,uint占用的空间为32位,即是4字节。
  • 在64位的机器上,uint占用的空间位64位,即是8字节。

声明uint类型的变量

var i uint

uint需要显式声明,如果要查看uint类型所占空间,可以使用bits.UintSize和unsafe.Sizeof()。同样,reflect.TypeOf()可以查看变量的类型。

package main

import (
    "fmt"
    "math/bits"
    "reflect"
    "unsafe"
)

func main() {
    sizeOfIntInBits := bits.UintSize
    fmt.Printf("%d bit\n", sizeOfIntInBits)
    
    var i uint
    fmt.Printf("%d 字节\n", unsafe.Sizeof(i))
    fmt.Printf("i的类型是%s\n", reflect.TypeOf(i))
}

uintptr

uintptr也是无符号的整型,它的空间大到足以容纳任何指针地址。因此,其所占空间和范围取决于平台。

  • 在32位的机器上,uint占用的空间为32位,取值范围:-2^31 to 2^31 -1。
  • 在64位的机器上,uint占用的空间位64位,取值范围:-2^63 to 2^63 -1。

特性:

  • uintptr可以和unsafe.Pointer相互转换
  • uintptr和其他整型一样进行数值运算
  • uintptr可以保存一个指针地址,但它只是一个值,并不引用任何对象

使用场景:

  • uintprt和unsafe.Pointer一起使用,用来做非安全的内存访问。
  • 用来保存指针地址用于打印和存储。因为uintprt只是一个值,不引用任何对象,也就是不影响对象的垃圾回收。

示例:

package main

import (
    "fmt"
    "unsafe"
)

type sample struct {
    a int
    b string
}

func main() {
    s := &sample{a: 1, b: "test"}
    
   //获取b在结构体s的地址
    p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.b))
}

这里需要说明下,在Go语言中,&表示取地址符号 , 取得某个变量的地址,返回的是变量的指针。 

 

Go日志框架logrus的基本用法

以package级别方式使用logrus

package main
import (
  "os"
  log "github.com/sirupsen/logrus"
)
func init() {
  // 设置日志格式
  log.SetFormatter(&log.JSONFormatter{})
  // 设置输出
  log.SetOutput(os.Stdout)
  // 设置日志级别
  log.SetLevel(log.WarnLevel)
}
func main() {
  log.WithFields(log.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")
  log.WithFields(log.Fields{
    "omg":    true,
    "number": 122,
  }).Warn("The group's number increased tremendously!")
  log.WithFields(log.Fields{
    "omg":    true,
    "number": 100,
  }).Fatal("The ice breaks!")
  // 设置日志默认的信息,所有使用contextLogger都会复用这些信息
  contextLogger := log.WithFields(log.Fields{
    "common": "this is a common field",
    "other": "I also should be logged always",
  })
  contextLogger.Info("I'll be logged with common and other field")
  contextLogger.Info("Me too")
}

以实例方式使用logrus

package main
import (
  "os"
  "github.com/sirupsen/logrus"
)
// 创建一个logrus示例
var log = logrus.New()
func main() {
  // 设置输出,这和package的方式有所不同,它是以属性的方式赋值
  log.Out = os.Stdout
  log.WithFields(logrus.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")
}

日志格式

logrus内置的日志格式有两种:

  • logrus.TextFormatter:纯文本
     
  • logrus.JSONFormatter:JSON
     

第三方的日志格式:FluentdFormatterlogstashprefixedzalgo

 

Go语言:变量的声明

在计算机语言中,变量是以可读的名称表示保存值的存储位置。变量存储的值都有对应的类型,它的类型决定了变量存储值的空间以及操作。在Go语言中,它的命名需要符合Go语言:标识符命名规则

变量在使用之前,需要先声明变量。Go语言提供了多种声明变量的方式,主要分为:

  1. 使用关键词var基本格式声明变量
  2. 多变量的声明
  3. 使用简短赋值符:=声明变量

下面介绍变量的多种声明方式

声明变量的基本格式

GO语言声明变量的基本格式是使用 var 关键字声明,如下:

var variable_name type = expression

其中:

  • variable_name:表示变量名,需把variable_name替换为实际使用的变量名。
  • type:表示变量类型,需把type替换为实际使用变量类型,如string,int等等。
  • expression:在等号右边,是对变量进行赋值的表达式,即对变量赋初始值。

示例:

package main

import "fmt"

func main() {
    var i int = 8
    fmt.Println(i)
}

示例中,定义了变量i,它的类型为整型int,赋初始值为8。所以打印输出结果为:

8

变量缺省值

在声明变量时,是可以不赋初始值,其格式为:

var variable_name type

当变量声明时,没有显式赋初始值,Go语言会使用变量类型的默认值初始化变量,该默认值也称为该类型的零值。因为默认值是在变量未显式初始化时赋值,也称为缺省值。如int类型的变量缺省值为0,string类型的变量缺省值为空字符串“”。

示例:

package main

import "fmt"

func main() {
    var i int
    fmt.Println(i)
}

输出结果:

0

因为变量的缺省值是由变量类型决定,所以当变量声明没有显式赋初始值时,必须指定其类型。

变量的类型推断

从变量的缺省值我们知道,类型决定了变量的缺省值。反过来,变量的值也能确定变量的类型。所以,Go语言中允许我们在声明变量时,不显式指定变量的类型。这种方式称为变量的类型推断。其格式是:

var variable_name = expression

以下为基本类型数值对应的Go语言类型

变量值类型Go语言的基本类型
整数int
浮点数float64
 复数complex128
字符串string
字符int32 或者 rune

对于Array,Pointer和Structure类型,Go语言也会根据值做类型推断。

示例:

package main

import "fmt"

func main() {
    var a = 123      //int
    var b = "bstring" //string
    var c = 5.6      //float64
    var d = true     //bool
    var e = 'a'      //rune
    var f = 3 + 5i   //complex128
    var g = mystruct{name: "test"}  //main.MyStruct

    fmt.Printf("Type: %T Value: %v\n", a, a)
    fmt.Printf("Type: %T Value: %v\n", b, b)
    fmt.Printf("Type: %T Value: %v\n", c, c)
    fmt.Printf("Type: %T Value: %v\n", d, d)
    fmt.Printf("Type: %T Value: %v\n", e, e)
    fmt.Printf("Type: %T Value: %v\n", f, f)
    fmt.Printf("Type: %T Value: %v\n", g, g)
}

type mystruct struct {
    name string
}

多变量声明

多变量声明根据多个变量的类型是否相同分为两种:

  1. 相同类型的变量声明
  2. 不同类型的变量声明

单个变量的缺省值声明和类型推断同样适用于多变量声明,在此就不重复说明。

相同类型的多变量声明

对于多个变量的声明,如果它们的类型是相同的,可以使用以下格式声明:

var name1, name2, …..,nameN type = value1, value2, …..,valueN

示例:

package main

import "fmt"

func main() {
    var a, b int = 8, 9
    var c,d int
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}

输出结果:

8
9
0
0

不同类型多变量声明

对于多个变量的声明,如果它们的类型不同,可以使用以下示例格式:

package main

import "fmt"

func main() {
    var (
        a int
        b int    = 10
        c string = "cstring"
    )

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}

先声明var关键词,接着在括号里声明变量,声明的变量可以赋初始值,如果不赋初始值,则使用类型的缺省值。

输出结果:

0
10
cstring

多变量声明的类型推断

多变量声明的类型推断格式:

var name1, name2,…,nameN = value1,value2,…,valueN

各个变量的类型由它各自的值确定,示例:

import "fmt"

func main() {
    var a, b, c = 10, true, "cstring"
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}

这种赋值常常使用在返回多个变量的方法上,如:

var f,err = os.Open(filename)

其中,f为os.Open返回的文件和错误err。

简短变量声明

Go 提供了另一种声明变量的方法,它使用 := 运算符。当使用 := 运算符时,var 关键词和类型信息都可以省略。以下是此类声明的格式:

 variable_name := expression

简短变量声明,既省略了var关键词,并且使用了Go语言的类型推断特性。以上类型推断的示例可以改写为:

package main

import "fmt"

func main() {
    a := 123      //int
    b := "bstring" //string
    c := 5.6      //float64
    d := true     //bool
    e := 'a'      //rune
    f := 3 + 5i   //complex128
    g := mystruct{name: "test"}  //main.MyStruct

    fmt.Printf("Type: %T Value: %v\n", a, a)
    fmt.Printf("Type: %T Value: %v\n", b, b)
    fmt.Printf("Type: %T Value: %v\n", c, c)
    fmt.Printf("Type: %T Value: %v\n", d, d)
    fmt.Printf("Type: %T Value: %v\n", e, e)
    fmt.Printf("Type: %T Value: %v\n", f, f)
    fmt.Printf("Type: %T Value: %v\n", g, g)
}

type mystruct struct {
    name string
}

:=运算符使用说明

使用:=运算符需要满足以下条件:

1. := 运算符仅在函数中可用。在函数之外是不允许的。

2. 使用 := 声明的变量不能多次使用 := 运算符重新声明。以下示例会报错:

a := 1
a := 2

3. := 运算符可声明多个变量。

a,b := 1, 2

4. 在多变量声明的情况下,如果至少一个变量是新的,则 := 也可以再次用于特定变量。

a, b := 1, 2
b, c := 3, 4

注意:第二行代码变量b虽然已被声明赋值,但由于c是新变量,所以允许使用:=运算符重新赋值。

Go语言:标识符命名规则

标识符实际是名称的另一种叫法,它包括了变量名、函数名、方法名、类型名、包名等。Go语言规定了有下面几点命名的语法要求。

 首字符必须是unicode字母或者下划线(_)

先要说明,unicode字母并非只是英文字母表里的26个字母,汉字也是属于Unicode字母。对Unicode字母感兴趣的可以参考:The Unicode Standard 8.0, Section 4.5

换句话说,除了下划线(_)外的标点符号和数字都是不能作为标识符的首字符。

符合规则的名称:

Name,store,_animal,中文,한국어

违反规则的名称

7_11,$dollars,¥人民币,#store

除首字母外,后面的字符可以是unicode字母,数字和下划线

这条规则限定了组成标识符的字符构成,不能包含如空格,标点符号。

符合规则的名称

_华为_,_7_11,iphone13,int

违反规则的名称

dollar$,blank space,You&Me

Go语言标识符里字母是区分大小写的

标识符区分大小写,如Name和name表示两个不同的标识符。

保留字不能使用作为标识符

以下25个关键词是作为Go语言的保留字,不能用作标识符。

break     default      func    interface  select
case      defer        go      map        struct
chan      else         goto    package    switch
const     fallthrough  if      range      type
continue  for          import  return     var

Go语言只限制了以上保留字不能作为标识符,其他符合规则的名称都可以使用,但Go语言还有一些是预声明和内置的常量名,类型名以及函数名:

常量名  true  false  iota  nil

类型名  int  int8  int16  int32  int64
        uint  uint8  uint16 uint32 uint64
        float32  float64  complex128  complex64
        bool byte  rune  string error
        
函数名  make len cap new append  copy  close  delete  
        complex real  imag panic recover

虽然这些名称不是保留字,为了避免产生歧义,强烈建议不要把他们作为标识符使用

标识符字符长度不限

虽然Go语言规则上没有限制,在Go语言官方在规范上是推荐使用短的变量名称,特别是作用域比较小的本地变量。如在for循环中使用i,而非indexOfLoop。

有这样一个规则:作用域小的变量,在不影响代码可读性的前提下,尽量使用短的名称;而应用在大作用域的变量,使用更具描述性的名称,这样名称相对长点

标识符可见性:要在package外公开的标记符必须以大写字母开头

Go语言中,标记符的可见范围是由标识符是定义所在的位置以及它名称首字母确定。

1. 位置:函数内定义,首字符无关

在函数内定义的标记符,可见范围也就是作用域是在函数内部,即函数本地。

2. 位置:函数外定义,首字母小写

在函数外定义的标记符,不管首字母是否大写,在声明为同一个package里的所有文件里是可见的。但对于不在同一package的文件,小写开头的标记符是不可见的。

如:test1.go 文件

package test
import "fmt"
func visibleInPkg() {
  fmt.Printf("inside package")
}
func VisibleOutside() {
  fmt.Printf("outside package")
}

test2.go文件

package test
func test2() {
  visibleInPkg()
}

3. 位置函数外定义,首字母为大小

如果要在package外使用定义的标记符,如函数,那么它的标记符首字母必须大写,如fmt的Printf()函数。

如:test3.go

package testoutside
import "test"
func test() {
  test.VisibleOutside()
}

规范:同一使用驼峰式命名

这是一个Go语言的命名规范,不是必须的规则。目前计算机语言对于由多个单词构成的名称,一般由两种做法:

  1. 驼峰式命名,分为小驼峰法和大驼峰法。
  2. 单词之间使用下划线隔开

小驼峰法第一个单词以小写字母开始,第二个单词的首字母大写或每一个单词的首字母都采用大写字母。如

firstName,lastName

相比小驼峰法,大驼峰法把第一个单词的首字母也大写了

FirstName,LastName

 

 

Go语言把map接口转换为google.protobuf.Struct

以下是go语言把map[string]interface{}转换为google.protobuf.Struct:

package pb
import (
"fmt"
"reflect"
st "github.com/golang/protobuf/ptypes/struct"
)
// ToStruct转换map[string]interface{} 为ptypes.Struct
func ToStruct(v map[string]interface{}) *st.Struct {
size := len(v)
if size == 0 {
return nil
}
fields := make(map[string]*st.Value, size)
for k, v := range v {
fields[k] = ToValue(v)
}
return &st.Struct{
Fields: fields,
}
}
// ToValue 转换interface{} 为ptypes.Value
func ToValue(v interface{}) *st.Value {
switch v := v.(type) {
case nil:
return nil
case bool:
return &st.Value{
Kind: &st.Value_BoolValue{
BoolValue: v,
},
}
case int:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case int8:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case int32:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case int64:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case uint:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case uint8:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case uint32:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case uint64:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case float32:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v),
},
}
case float64:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: v,
},
}
case string:
return &st.Value{
Kind: &st.Value_StringValue{
StringValue: v,
},
}
case error:
return &st.Value{
Kind: &st.Value_StringValue{
StringValue: v.Error(),
},
}
default:
// 回退为其他类型
return toValue(reflect.ValueOf(v))
}
}
func toValue(v reflect.Value) *st.Value {
switch v.Kind() {
case reflect.Bool:
return &st.Value{
Kind: &st.Value_BoolValue{
BoolValue: v.Bool(),
},
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v.Int()),
},
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: float64(v.Uint()),
},
}
case reflect.Float32, reflect.Float64:
return &st.Value{
Kind: &st.Value_NumberValue{
NumberValue: v.Float(),
},
}
case reflect.Ptr:
if v.IsNil() {
return nil
}
return toValue(reflect.Indirect(v))
case reflect.Array, reflect.Slice:
size := v.Len()
if size == 0 {
return nil
}
values := make([]*st.Value, size)
for i := 0; i < size; i++ {
values[i] = toValue(v.Index(i))
}
return &st.Value{
Kind: &st.Value_ListValue{
ListValue: &st.ListValue{
Values: values,
},
},
}
case reflect.Struct:
t := v.Type()
size := v.NumField()
if size == 0 {
return nil
}
fields := make(map[string]*st.Value, size)
for i := 0; i < size; i++ {
name := t.Field(i).Name
if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
fields[name] = toValue(v.Field(i))
}
}
if len(fields) == 0 {
return nil
}
return &st.Value{
Kind: &st.Value_StructValue{
StructValue: &st.Struct{
Fields: fields,
},
},
}
case reflect.Map:
keys := v.MapKeys()
if len(keys) == 0 {
return nil
}
fields := make(map[string]*st.Value, len(keys))
for _, k := range keys {
if k.Kind() == reflect.String {
fields[k.String()] = toValue(v.MapIndex(k))
}
}
if len(fields) == 0 {
return nil
}
return &st.Value{
Kind: &st.Value_StructValue{
StructValue: &st.Struct{
Fields: fields,
},
},
}
default:
// 最后排序
return &st.Value{
Kind: &st.Value_StringValue{
StringValue: fmt.Sprint(v),
},
}
}
}

GPU架构学习资源

这里收藏了gpu学习的网站,pdf等等资源: