Go语言初尝

Posted 编程大观园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言初尝相关的知识,希望对你有一定的参考价值。

如果把 C 变成 C++, 那么 C 就消失了。

概述

对于语言设计之争, 唯一需要牢记的一句话是: 如果把 C 变成 C++, 那么 C 就消失了。

Go 是一个轻量级的简洁的支持并发的现代语言, 可以用于探索性个人项目, 这是我想学这门语言的主要原因。 对于有一定编程经验的人来说, 学习一种新语言的方式是, 先概览下语言特性, 然后编写一个中等规模的程序, 尽可能地运用到大部分重要特性。

Go 的主要语言特性:

  • 静态类型 + 类型推导
  • 简化的控制结构: if , for , switch
  • 可命名的多返回值的函数
  • 内置容器支持: 数组、Slice、Map
  • 简化的指针与引用
  • 简化的对象特性: 结构体与非侵入式接口
  • 内置的并发支持: go, Goroutine & Channel
  • 反射、匿名函数与闭包;
  • 便捷地 CGo 交互;
  • 内置 GC ;
  • 包管理。

针对熟练 Java 开发者,快速上手 Go 语言。

学习新语言的最佳方式,就是找一个实际项目来研究,再写一个实用程序来练手。

Go 语法

1.  变量名在前,变量类型在后;变量名(方法名、函数名、类名)采用驼峰形式。比如:

type JsonResp struct 
    Msg  string      `json:"msg"`
    Data interface `json:"data"`

2.  语句结束不用分号。

3.  条件语句不用括号。

if runMode == engine_common.RUNMODE_TRACE 
    processor = append(processor, sendMongo.NewSendDetectToMongo(w.mongoDao))

4.  强制类型转换

edgeName := p["type"].(string)

var a int32  = 10
var b int64 = int64(a)

var a int =10
var b *int =&a
var c *int64 = (*int64)(unsafe.Pointer(b))    

5.  定义枚举常量,可以用于 switch 语句;常量名全大写。

const 


const (
    RUNMODE_TRACE   = "trace"   // 联调模式,日志输出至kafka、日志、控制台
    RUNMODE_DEBUG   = "debug"   // 本地调试模式,日志输出至日志、控制台
    RUNMODE_RELEASE = "release" // 发布模式,用于正式发布,仅输出至kafka
    RUNMODE_MOCK    = "mock"    // 测试模式,用于测试相关流程,输出到go channel
)

switch runMode 
    case engine_common.RUNMODE_TRACE:
        processor = append(processor, aggregator.NewIncidentMsgAggregatorWithSink(w.config.AggregationTime,
            sink.NewKafkaSink(w.incidentKafka),
            sink.NewLogSink()))

    case engine_common.RUNMODE_RELEASE:
        processor = append(processor, aggregator.NewIncidentMsgAggregatorWithSink(w.config.AggregationTime,
            sink.NewKafkaSink(w.incidentKafka)))

    case engine_common.RUNMODE_DEBUG:
        a.AddSink(sink.NewConsoleSink())
        a.AddSink(sink.NewLogSink())

    case engine_common.RUNMODE_MOCK:
        a.AddSink(sink.NewLogSink())
        a.AddSink(sink.NewChannelSink(param.MockChannel))
    

6.  定义通用对象

type Any interface
type List []Any

7.  遍历列表

Path []map[string]common.Any `json:"path"`

for _, p := range ctx.Detect.Path 
    if p["type"] == "DETECT_POINT" 
        detect_id = p["dst_id"].(string)
    

8.  定义映射

var m = map[int]string
    ERROR_SUCCESS:          "ok",
    ERROR_FAIL:             "fail",
    ERROR_INVALID_PATH:     "请求路径参数不合法",
    ERROR_INVALID_QUERY:    "请求查询参数错误",
    ERROR_RESULT_NOT_EXIST: "没有找到符合条件的记录",


// 多重 map
elements map[string]map[string]common.Any

// 创建 map
edge_prop := make(map[string]common.Any)

9.  定义函数

左括号必须与 func 在同一行。函数名首字母大写。

func 方法名(方法签名)返回对象 


func GetMsg(code int) string 
    msg, ok := m[code]
    if ok 
        return msg
    

    return m[ERROR_SUCCESS]

10.  定义类、构造器、方法

类名首字母大写。

type 类名或结构名 struct 


func New类名() *类名 
      return &类名属性名:属性值


func (m *类名) 方法名(方法签名) 返回值类型 


示例:

// EngineConfig 配置项
type EngineConfig struct 
    GraphEngineComponent                          // 组件配置
    GraphEngineRule                               // 本地规则配置,初次从本地加载的数据
    CustomConfig         `toml:"custom_config"`   // 自定义配置,控制参数
    LogConfig            `toml:"log_config"`      // 日志配置
    ServiceConfig        `toml:"service_config"`  // 服务配置
    MonitorService       `toml:"monitor_service"` // 監控

    // 合并本地和云端配置后的规则配置参数,云端参数优先
    graphEngineUpdatedRule atomic.Value

    // 单例nacos客户端,Go 保留指针,但不能对指针运算,只能引用
    nacosClient *nacos.Client


type CenterServer struct 
    servers map[string] ipc.Server players []*Player
    rooms []*Room
    mutex sync.RWMutex


func NewCenterServer() *CenterServer  
    servers := make(map[string] ipc.Server) players := make([]*Player, 0)
    return &CenterServerservers:servers, players:players 


# 定义类的方法, (m *EngineConfig) 是对象的隐藏指针。
func (m *EngineConfig) GetIncidentMaxRange() int64 

    timeSlice := m.graphEngineUpdatedRule.Load().(GraphEngineRule).TimeSliceData
    if timeSlice.MaxRange.Duration == 0 
        return int64(time.Minute * 5 / time.Millisecond) // 默认间隔5min
     else 
        return int64(timeSlice.MaxRange.Duration / time.Millisecond)
    

12.  定义接口

只要实现这个接口的方法,就能赋值给这个接口的引用。即无论是黑猫还是白狗,能抓住老鼠的就是鼠的天敌。

type IWorkFlowFactory interface 
    GetName() string
    Clean() bool
    Create(name string, params interface) (IWorkFlow, error)


type WorkFlowFactory struct 
    config        *config.EngineConfig
    globalContext context.Context

    mongoDao *mongo.MongoDao // 仅trace模式时会用到

    // code...


func (w *WorkFlowFactory) GetName() string 
    return "WorkFlowFactory"


func (w *WorkFlowFactory) Clean() bool 

    // code...

    log.Info("workflow factory clean successful")
    return true


func (w *WorkFlowFactory) Create(name string, params interface) (define.IWorkFlow, error) 
    // code

13.  类型自动推导: a := 5

14.  错误处理

if param, ok := params.(*service_define.StartParam); !ok 
    return nil, errors.New("Input type error ")


nebulaDao := dao.NewNebulaDaoWithSpace(w.nebulaClient, nebulaSpace)
if nil == nebulaDao 
    return nil, errors.New("Init nebula dao error ")

15. 格式化

fmt.Errorf("detect.path[%d].dst_id=%v missing in detect.element, detectId=%v", j, p["dst_id"].(string), ctx.Detect.Id)

编程实战

下面的程序用于计算一个目录下所有文件或目录的大小。

$ go build -o dirsize dirsize_module.go time2.go

$ dirsize

dirsize_module.go


package main

import (
    "fmt"
//    "log"
    "os"
    "sort"
    "time"
)

import "C"

type ShortFileInfo struct 
    fileName string
    size     int64


type FileInfoSlice []ShortFileInfo

func (fileInfo *ShortFileInfo) Desc() string 
    return fmt.Sprintf("%s:%s", fileInfo.fileName, readableSize(fileInfo.size))


func (fileInfos FileInfoSlice) Len() int 
    return len(fileInfos)


func (fileInfos FileInfoSlice) Swap(i, j int) 
    fileInfos[i], fileInfos[j] = fileInfos[j], fileInfos[i]


func (fileInfos FileInfoSlice) Less(i, j int) bool 
    return fileInfos[i].size > fileInfos[j].size


type IOError struct 
     msg string
     err error


func (ioe IOError) Error() string 
    return "Error: " + ioe.msg + "\\nCause: " + ioe.err.Error()


func produceFiles(dirName string) (fileInfos []os.FileInfo, e error) 
    path, err := os.Open(dirName)
    if err != nil 
        //log.Fatal(err)
        return nil , IOError"failed to open " + dirName, err
    
    defer path.Close()
    fileInfos, readerr := path.Readdir(0)
    if readerr != nil 
        //log.Fatal(readerr)
        return nil, IOError"failed to read " + dirName, readerr
    
    return fileInfos, nil


func procFile(fileInfo os.FileInfo, baseDirName string, channelBuffer chan ShortFileInfo) 
    var filesize int64
    fileName := fileInfo.Name()
    if fileInfo.IsDir() 
        filesize = totalFilesizeInDir(fileInfo, baseDirName)
     else 
        filesize = fileInfo.Size()
    
    shortFileInfo := ShortFileInfofileName, filesize
    fmt.Println(time.Now().String() + " store: " + shortFileInfo.Desc())
    channelBuffer <- shortFileInfo


func totalFilesizeInDir(fileInfo os.FileInfo, baseDirName string) int64 
    var filesize int64 = 0
    fileInfos, _ := produceFiles(baseDirName + "/" + fileInfo.Name())
    for _, subfileInfo := range fileInfos 
        if subfileInfo.IsDir() 
            filesize += totalFilesizeInDir(subfileInfo, baseDirName+"/"+fileInfo.Name())
         else 
            filesize += subfileInfo.Size()
        
    
    return filesize


func sleep(ns int) 
    time.Sleep(time.Duration(time.Second) * time.Duration(ns))


const (
    B  int64 = 1
    KB int64 = 1024
    MB int64 = 1024 * 1024
    GB int64 = 1024 * 1024 * 1024
    TB int64 = 1024 * 1024 * 1024 * 1024
)

const formatF string = "%8.4f"

func readableSize(sizeInBytes int64) string 
    switch 
    case B <= sizeInBytes && sizeInBytes < KB:
        return fmt.Sprintf("%dB", sizeInBytes)
    case KB <= sizeInBytes && sizeInBytes < MB:
        return fmt.Sprintf(formatF+"KB", float64(sizeInBytes)/float64(KB))
    case MB <= sizeInBytes && sizeInBytes < GB:
        return fmt.Sprintf(formatF+"MB", float64(sizeInBytes)/float64(MB))
    case GB <= sizeInBytes && sizeInBytes < TB:
        return fmt.Sprintf(formatF+"GB", float64(sizeInBytes)/float64(GB))
    case TB <= sizeInBytes:
        return fmt.Sprintf(formatF+"TB", float64(sizeInBytes)/float64(TB))
    default:
        return "0"
    


//export mainProc
func mainProc() 

    start_1 := time.Now().Unix()

    baseDirName := "/home/lovesqcc"
    //baseDirName := "/notExist"
    //baseDirName := "/"

    fileList, err := produceFiles(baseDirName)
    if err != nil 
       fmt.Println(err.Error())
       os.Exit(1)
    
    fileNumber := len(fileList)
    channelBuffer := make(chan ShortFileInfo, fileNumber)
    fileInfoMap := make(map[string]int64, fileNumber)

    for _, fileInfo := range fileList 
        go procFile(fileInfo, baseDirName, channelBuffer)
    

    var fileInfos = make([]ShortFileInfo, fileNumber+1)

    timeout := make(chan int, 1)
        go func() 
        secs := 3
        sleep(secs)
        timeout <- secs
    ()

    for count := 0; count <= fileNumber; 
        select 
        case fileInfo := <-channelBuffer:
            fmt.Println(time.Now().String() + " fetch: " + fileInfo.Desc())
            fileInfoMap[fileInfo.fileName] = fileInfo.size
            fileInfos[count] = fileInfo
            count++
        case secs := <- timeout:
            fmt.Printf("%d s timout ! Exit Loop\\n", secs)
            os.Exit(1)
        default:
            if count == fileNumber 
                close(channelBuffer)
            
            fmt.Println("Waiting for data ...")
        
    
    sort.Sort(FileInfoSlice(fileInfos))

    for _, fileInfo := range FileInfoSlice(fileInfos) 
        fmt.Println("File " + fileInfo.fileName + " : " + readableSize(fileInfo.size))
    

    var totalSize int64
    totalSize = 0
    for _, filesize := range fileInfoMap 
        totalSize += filesize
    
    fmt.Printf("Total size in %s:%dB %s\\n", baseDirName, totalSize, readableSize(totalSize))

    end_1 := time.Now().Unix()
    fmt.Printf("start=%d, end=%d\\n",start_1, end_1)
    fmt.Printf("Time cost: %dms\\n", (end_1-start_1)*1000)


time2.go

package main

/*
#include<stdio.h>
#include<time.h>

extern int mainProc(); 

int printTimeCost() 
   printf("exec printTimeCost");
   time_t start = 0, end = 0;
   long duration = 0;
   start = time(NULL);
   mainProc();
   end = time(NULL);
   duration = (end - start) *1000;
   return duration;


*/
import "C"
import "fmt"
func main()  
    fmt.Printf("cost: %dms\\n", C.int(C.printTimeCost()))




参考资料

以上是关于Go语言初尝的主要内容,如果未能解决你的问题,请参考以下文章

初尝Swift-用Playground来学习Swift语言

Kotlin 初尝之项目实践总结

初尝C++的世界

初尝C++的世界

初尝 JFinal 项目

DL踩坑:初尝过拟合