• [技术干货] 50年前,Hello World发明者第一次提交的Go代码长这样……[转载]
    原文链接:https://blog.csdn.net/csdndevelopers/article/details/122941245作为Unix的研发者与传奇代码“Hello World”的发明者,普林斯顿大学计算机科学系教授布莱恩·克尼汉(Brian W. Kernighan)是编程界的传奇人物。他曾参与创造了AMPL(数学编程语言)和AWK(文本处理工具),也曾和C语言之父丹尼斯·里奇(Dennis MacAlistair Ritchie)共同编写《C程序设计语言》(The C Programming Language)。不仅如此,据调查,Brian W. Kernighan竟在1972年7月18日首次提交Go代码。commit 7d7c6a97f815e9279d08cfaea7d5efb5e90695a8Author: Brian Kernighan <bwk>Date:   Tue Jul 18 19:05:45 1972 -0500    hello, world    R=ken    DELTA=7  (7 added, 0 deleted, 0 changed)diff --git a/src/pkg/debug/macho/testdata/hello.b b/src/pkg/debug/macho/testdata/hello.bnew file mode 100644index 0000000000..05c4140424--- /dev/null+++ b/src/pkg/debug/macho/testdata/hello.b@@ -0,0 +1,7 @@+main( ) {+       extrn a, b, c;+       putchar(a); putchar(b); putchar(c); putchar('!*n');+} +a 'hell';+b 'o, w';+c 'orld';在接下来的十多年中,布莱恩仍在不断进行修改:1974年1月20日01:02:03:布莱恩提交了convert to C,将B语言的Hello World改为C语言实现;1988年4月1日02:02:04:提交convert to Draft-Proposed ANSI C,将HelloWorld改为草案中提出的ANSI C实现;1988年4月1日02:03:04:提交last-minute fix: convert to ANSI C,最后修改,改为ANSI C实现。1988年,布莱恩团队决定暂停更新。直到20年后的2007年9月,罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)和肯·汤普逊(Ken Thompson)开始设计Go,提交了Go spec starting point。直到现在,这些早期提交的文件(即最终的ANSI C 版本)仍保留在Go repo中。【参考资料】https://repography.com/blog/go-first-commithttps://github.com/golang/go/commit/0bb0b61d6a85b2a1a33dcbc418089656f2754d32https://github.com/golang/go/commit/0744ac969119db8a0ad3253951d375eb77cfce9ehttps://github.com/golang/go/commit/d82b11e4a46307f1f1415024f33263e819c222b8
  • [交流吐槽] 老板能不能上个JBL GO2 音乐金砖,家里迫切需要一个。
    老板,能不能上个JBL GO2 音乐金砖,家里迫切需要一个。谢谢了。
  • [问题求助] 【openGauss】编译openGauss-prometheus-exporter 有要求go必须是多少版本以上的吗?
    编译openGauss-prometheus-exporter 有要求go必须是多少版本以上的吗?
  • [技术干货] goland安装教程[转载]
    go语言有很多的插件来编译程序  其中最为好的就是goland 转载一篇关于安装goland的文章希望大家喜欢原文链接:https://blog.csdn.net/weixin_42809940/article/details/82423171go语言环境安装下载地址:https://golang.google.cn/dl/goland软件安装下载地址:http://www.jetbrains.com/go/download/#section=windowsgoland许可证认证码可以到网站http://idea.lanyus.com/查询认证码。github 上clone项目配置系统的环境变量GOPATH为任意一个目录,用于存放src下的package目录再然后把项目放入GOPTH目录下的src目录下使用go get 安装各种包golang.org/x/net 安装方法为了使包的导入方式不变,我们需要在src目录下面构造目录结构$mkdir -p  $GOPATH/src/golang.org/x/$cd $GOPATH/src/golang.org/x/$git clone https://github.com/golang/net.git net$go install net执行go install之后没有提示,就说明安装好了。windows安装go-sqlite3失败,提示找不到gccwindows安装go-sqlite3失败,提示找不到gcc go get github.com/mattn/go-sqlite3时失败,提示exec: “gcc”: executable file not found in %PATH%,是因为没有安装gcc。 去http://tdm-gcc.tdragon.net/download下载一个,32位windows下载 tdm-gcc-4.8.1-3.exe,64位下载 tdm64-gcc-4.8.1-3.exe注:为保证能够成功编译,建议安装所有的包。安装完成以后,配置环境变量path中添加gcc安装目录,C:\TDM-GCC-64\bin运行go get 指令 提示unrecognized import path运行指令如下:go get google.golang.org/grpc错误提示:package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 74.125.28.14:443: i/o timeout)解决方法:1、建立相关文件夹mkdir -p $GOPATH/src/google.golang.org/2、命令行打开文件夹cd $GOPATH/src/google.golang.org3、从Github上克隆其他的仓库 git clone https://github.com/Agzs/grpc grpc4、 安装仓库cd $GOPATH/src/go install google.golang.org/grpc5、 结束最后运行成功。
  • [技术干货] GO语言特性之自动垃圾回收
    Go语言作为一门全新的静态类型开发语言,与当前的开发语言相比具备众多令人兴奋不已的新特性。自动垃圾回收我们可以先看下不支持垃圾回收的语言的资源管理方式,以下为一小段C语言代码void foo() { char* p = new char[128]; ... // 对p指向的内存块进行赋值 func1(p); // 使用内存指针 delete[] p; }       各种非预期的原因,比如由于开发者的疏忽导致最后的delete语句没有被调用,都会引发经典而恼人的内存泄露问题。假如该函数被调用得非常频繁,那么我们观察该进程执行时,会发现该进程所占用的内存会一直疯长,直至占用所有系统内存并导致程序崩溃,而如果泄露的是系统资源的话,那么后果还会更加严重,最终很有可能导致系统崩溃。      手动管理内存的另外一个问题就是由于指针的到处传递而无法确定何时可以释放该指针所指向的内存块。假如代码中某个位置释放了内存,而另一些地方还在使用指向这块内存的指针,那么这些指针就变成了所谓的“野指针”(wild pointer)或者“悬空指针”(dangling pointer),对这些指针进行的任何读写操作都会导致不可预料的后果。      由于其杰出的效率,C和C++语言在非常长的时间内都作为服务端系统的主要开发语言,比如Apache、Nginx和MySQL等著名的服务器端软件就是用C和C++开发的。然而,内存和资源管理一直是一个让人非常抓狂的难题。服务器的崩溃十有八九就是因为不正确的内存和资源管理导致,更讨厌的是这种内存和资源管理问题即使被发现了,也很难定位到具体的错误地点,导致数程序员通宵达旦地调试程序。      这个问题在多年里被不同人用不同的方式来试图解决,并诞生了一些非常著名的内存检查工具,比如Rational Purify、Compuware BoundsChecker和英特尔的Parallel Inspector等。从设计方法的角度也衍生了类似于内存引用计数之类的方法(通常被称为“智能指针”),后续在Windows平台上标准化的COM出现的一个重要原因就是为了解决内存管理的难题。但是事实证明,这些工具和方法虽然能够在一定程度上辅助开发者,但并没法让开发者避免通宵调试这样又苦又累的工作。      到目前为止,内存泄露的最佳解决方案是在语言级别引入自动垃圾回收算法(Garbage Collection,简称GC)。所谓垃圾回收,即所有的内存分配动作都会被在运行时记录,同时任何对该内存的使用也都会被记录,然后垃圾回收器会对所有已经分配的内存进行跟踪监测,一旦发现有些内存已经不再被任何人使用,就阶段性地回收这些没人用的内存。当然,因为需要尽量最小化垃圾回收的性能损耗,以及降低对正常程序执行过程的影响,现实中的垃圾回收算法要比这个复杂得多,比如为对象增加年龄属性等,但基本原理都是如此。      自动垃圾回收在C/C++社区一直作为一柄双刃剑看待,虽然到C++0x(后命名为C++11)正式发布时,这个呼声颇高的特性总算是被加入了,但按C++之父的说法,由于C++本身过于强大,导致在C++中支持垃圾收集变成了一个困难的工作。假如C++支持垃圾收集,以下的代码片段在运行时就会是一个严峻的考验:int* p = new int; p += 10; // 对指针进行了偏移,因此那块内存不再被引用 // …… 这里可能会发生针对这块int内存的垃圾收集 …… p -= 10; // 咦,居然又偏移到原来的位置 *p = 10; // 如果有垃圾收集,这里就无法保证可以正常运行了      微软的C++/CLI算是用一种偏门的方式让C++程序员们有机会品尝一下垃圾回收功能的鲜美味道。在C/C++之后出现的新语言,比如Java和C#等,基本上都已经自带自动垃圾回收功能。      Go语言作为一门新生的开发语言,当然不能忽略内存管理这个问题。又因为Go语言没有C++这么“强大”的指针计算功能,因此可以很自然地包含垃圾回收功能。因为垃圾回收功能的支持,开发者无需担心所指向的对象失效的问题,因此Go语言中不需要delete关键字,也不需要free()方法来明确释放内存。例如,对于以上的这个C语言例子,如果使用Go语言实现,我们就完全不用考虑何时需要释放之前分配的内存的问题,系统会自动帮我们判断,并在合适的时候(比如CPU相对空闲的时候)进行自动垃圾收集工作。
  • [其他] 训练作业日志提示&quot;No CUDA-capable device is detected&quot;解决方法
    #### 问题现象 在程序运行过程中,出现如下类似错误。 1.‘failed call to cuInit: CUDA\_ERROR\_NO\_DEVICE: no CUDA-capable device is detected’ 2.‘No CUDA-capable device is detected although requirements are installed’ #### 原因分析 出现该问题的可能原因如下: - 用户/训练系统,将CUDA\_VISIBLE\_DEVICES传错了,检查一下CUDA\_VISIBLE\_DEVICES变量是否正常。 - 用户选择了1/2/4卡这些规格的作业,然后设置了CUDA\_VISIBLE\_DEVICES=‘1’这种类似固定的卡ID号,与实际选择的卡ID不匹配。 #### 处理方法 1. 尽量代码里不要去修改CUDA\_VISIBLE\_DEVICES变量,用系统默认里面自带的。 2. 如果必须指定卡ID,需要注意一下1/2/4规格下,指定的卡ID与实际分配的卡ID不匹配的情况。 3. 如果上述方法还出现了错误,可以去notebook里面调试打印CUDA\_VISIBLE\_DEVICES变量,或者用以下代码测试一下,查看结果是否返回的是True。 import torch torch.cuda.is\_available()
  • [技术干货] Golang极简入门教程(三):并发支持
    这篇文章主要介绍了Golang极简入门教程(三):并发支持,本文讲解了goroutine线程、channel 操作符等内容,需要的朋友可以参考下Golang 运行时(runtime)管理了一种轻量级线程,被叫做 goroutine。创建数十万级的 goroutine 是没有问题的。范例:复制代码代码如下:package main import (    "fmt"    "time") func say(s string) {    for i := 0; i < 5; i++ {        time.Sleep(100 * time.Millisecond)        fmt.Println(s)    }} func main() {    // 开启一个 goroutine 执行 say 函数    go say("world")    say("hello")}我们使用 channel 和 goroutine 通讯。channel 中是一种带有类型的通道,被用于接收和发送特定类型的值。操作符 <- 被叫做 channel 操作符(这个操作符中箭头表明了值的流向):复制代码代码如下:// 发送 v 到 channel chch <- v// 接收 channel ch 中的值并赋值给 vv := <-ch使用 channel 和 goroutine 通讯能够避免显式使用锁机制,通过 channel 发送和接收值时默认是阻塞的。通过 make 函数创建 channel:复制代码代码如下:// int 指定 channel 收发值的类型为 intch := make(chan int)一个完整的例子:复制代码代码如下:package main import "fmt" // 计算数组 a 中所有元素值之和func sum(a []int, c chan int) {    sum := 0    for _, v := range a {        sum += v    }    // 计算结果发送到 channel c    c <- sum} func main() {    a := []int{7, 2, 8, -9, 4, 0}     // 创建 channel c    c := make(chan int)     go sum(a[:len(a)/2], c)    go sum(a[len(a)/2:], c)     // 接收两个 goroutine 发送的计算结果    x, y := <-c, <-c     fmt.Println(x, y, x+y)}package main import "fmt" // 计算数组 a 中所有元素值之和func sum(a []int, c chan int) {    sum := 0    for _, v := range a {        sum += v    }    // 计算结果发送到 channel c    c <- sum} func main() {    a := []int{7, 2, 8, -9, 4, 0}     // 创建 channel c    c := make(chan int)     go sum(a[:len(a)/2], c)    go sum(a[len(a)/2:], c)     // 接收两个 goroutine 发送的计算结果    x, y := <-c, <-c     fmt.Println(x, y, x+y)}channel 可以带有一个缓冲区(buffer)来缓存被传递的值,向 channel 中发送时只有缓冲区满的情况下会阻塞,接收 channel 中的值时只有在缓冲区空的情况下阻塞:复制代码代码如下:package main import "fmt" func main() {    // 创建 channel,缓冲区长度为 2    c := make(chan int, 2)    // 由于 channel 的缓冲区长度为 2    // 因此发送不会阻塞    c <- 1    c <- 2    fmt.Println(<-c)    fmt.Println(<-c)}发送者可以调用 close 来关闭 channel,接收者可以检测到 channel 是否被关闭:复制代码代码如下:// 这里的 ok 为 false 表示已经没有值可以接收了,并且 channel 被关闭了v, ok := <-ch不要向已经关闭的 channel 发送值了(will cause a panic)。我们可以使用 for range 来接收 channel 中的值:复制代码代码如下:package main import "fmt" func fibonacci(n int, c chan int) {    x, y := 0, 1    for i := 0; i < n; i++ {        c <- x        x, y = y, x+y    }    // 必须要关闭 c    close(c)} func main() {    c := make(chan int, 10)    go fibonacci(cap(c), c)    // 这里 for 和 range 组合使用    // 不断的接收 c 中的值一直到它被关闭    for i := range c {        fmt.Println(i)    }}通常来说,我们不需要主动的关闭 channel。但有时候接收者必须被告知已经没有值可以接收了,这时候主动关闭是必要的,例如终止 for range 循环。使用 select 语句可以让一个 goroutine 等待多个通讯操作。select 会阻塞直到某个 case 能够运行,如果同时存在多个可执行的,那么将随机选择一个:复制代码代码如下:package main import "fmt" func fibonacci(c, quit chan int) {    x, y := 0, 1    for {        select {        case c <- x:            x, y = y, x+y        // 控制此线程退出        case <-quit:            fmt.Println("quit")            return        }    }} func main() {    c := make(chan int)    quit := make(chan int)    go func() {        for i := 0; i < 10; i++ {            fmt.Println(<-c)        }        quit <- 0    }()    fibonacci(c, quit)}select 中的 default 会在没有任何 case 可执行时执行(类似于 switch):复制代码代码如下:package main import (    "fmt"    "time") func main() {    // 创建一个 tick channel    // 在 100 毫秒后会向 tick channel 中发送当前时间    tick := time.Tick(100 * time.Millisecond)    // 创建一个 boom channel    // 在 500 毫秒后会向 boom channel 中发送当前时间    boom := time.After(500 * time.Millisecond)    for {        select {        case <-tick:            fmt.Println("tick.")        case <-boom:            fmt.Println("BOOM!")            return        default:            fmt.Println("    .")            time.Sleep(50 * time.Millisecond)        }    }}
  • [技术干货] Golang极简入门教程(二):方法和接口
    这篇文章主要介绍了Golang极简入门教程(二):方法和接口,本文同时讲解了错误、匿名域等内容,需要的朋友可以参考下方法在 Golang 中没有类,不过我们可以为结构体定义方法。我们看一个例子:复制代码代码如下:package main import (    "fmt"    "math") type Vertex struct {    X, Y float64} // 结构体 Vertex 的方法// 这里的方法接收者(method receiver)v 的类型为 *Vertexfunc (v *Vertex) Abs() float64 {    return math.Sqrt(v.X*v.X + v.Y*v.Y)} func main() {    v := &Vertex{3, 4}    fmt.Println(v.Abs())}在这里方法的接收者使用指针类型而非值类型主要出于以下几点考虑(类似 C/C++ 等语言):1.避免方法每次调用时,对接收者的不必要的拷贝2.在方法内可以修改接收者的值我们可以为任意类型定义方法,但以下情况除外:1.如果类型定义在其他包中,不能为其定义方法2.如果类型是基础类型,不能为其定义方法复制代码代码如下:package main import (    "fmt"    "math") // 定义一个类型 MyFloattype MyFloat float64 // 注意此方法关联的类型是 MyFloat 而不是 *MyFloatfunc (f MyFloat) Abs() float64 {    if f < 0 {        return float64(-f)    }    return float64(f)} func main() {    f := MyFloat(-math.Sqrt2)    fmt.Println(f.Abs())}接口(interface)接口也是一种类型(就像结构体一样)。一个接口类型包含了一组方法,一个接口类型能够持有那些实现了这些方法的值。范例:复制代码代码如下:// 定义接口 Absertype Abser interface {    Abs() float64} // 定义结构体 Vertextype Vertex struct {    X, Y float64} // 实现方法 Absfunc (v *Vertex) Abs() float64 {    return math.Sqrt(v.X*v.X + v.Y*v.Y)} func main() {    v := Vertex{3, 4}    // 成功,能够持有 *Vertex 类型的值    var a Abser = &v    // 出错,不能持有 Vertex 类型的值    // 因为在 *Vertex 上定义了方法 Abs,而未在 Vertex 上定义    var b Abser = v}错误Golang 提供了一个 error 接口:复制代码代码如下:type error interface {    Error() string}我们通过 os.Open 函数来了解一下 error 的用法:复制代码代码如下:// 此函数用于打开一个文件// 返回的第二个值为 error 类型func Open(name string) (file *File, err error)简单的例子:复制代码代码如下:package main import (    "fmt"    "os") func main() {    _, err := os.Open("test.txt")    // 如果 err 不为 nil 表示存在错误    if err != nil {        fmt.Println(err)    }}创建一个 error 值的最简单方式是使用 errors.New 函数:复制代码代码如下:func Sqrt(f float64) (float64, error) {    if f < 0 {        // 出错时返回一个错误        return 0, errors.New("math: square root of negative number")    }    // ...}我们也可以定义一个新的 error 的实现(也就是实现接口 error):复制代码代码如下:type NegativeSqrtError float64 func (f NegativeSqrtError) Error() string {    return fmt.Sprintf("math: square root of negative number %g", float64(f))}匿名域结构体中可以存在只有类型而没有名字的域,它们被叫做匿名域。例如:复制代码代码如下:struct {    T1    *T2}一个结构体的匿名域中的域或者方法可以被此结构体实例直接访问:复制代码代码如下:package main import "fmt" type Car struct {    wheelCount int} func (car *Car) numberOfWheels() int {    return car.wheelCount} type Ferrari struct {    Car} func main() {    f := Ferrari{Car{4}}    fmt.Println("A Ferrari has this many wheels: ", f.numberOfWheels())}
  • [问题求助] AOS服务有提供go语言的SDK吗?
    项目准备使用AOS服务,请教下有没有go语言的SDK可以用?感谢
  • [技术干货] go 并发
    Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。goroutine 语法格式:go 函数名( 参数列表 )例如:go f(x, y, z)开启一个新的 goroutine:f(x, y, z)Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。实例package mainimport (        "fmt"        "time")func say(s string) {        for i := 0; i < 5; i++ {                time.Sleep(100 * time.Millisecond)                fmt.Println(s)        }}func main() {        go say("world")        say("hello")}执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:worldhellohelloworldworldhellohelloworldworldhello通道(channel)通道(channel)是用来传递数据的一个数据结构。通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。ch <- v    // 把 v 发送到通道 chv := <-ch  // 从 ch 接收数据           // 并把值赋给 v声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:ch := make(chan int)注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:实例package mainimport "fmt"func sum(s []int, c chan int) {        sum := 0        for _, v := range s {                sum += v        }        c <- sum // 把 sum 发送到通道 c}func main() {        s := []int{7, 2, 8, -9, 4, 0}        c := make(chan int)        go sum(s[:len(s)/2], c)        go sum(s[len(s)/2:], c)        x, y := <-c, <-c // 从通道 c 中接收        fmt.Println(x, y, x+y)}输出结果为:-5 17 12通道缓冲区通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:ch := make(chan int, 100)带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。实例package mainimport "fmt"func main() {    // 这里我们定义了一个可以存储整数类型的带缓冲通道        // 缓冲区大小为2        ch := make(chan int, 2)        // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据        // 而不用立刻需要去同步读取数据        ch <- 1        ch <- 2        // 获取这两个数据        fmt.Println(<-ch)        fmt.Println(<-ch)}执行输出结果为:Go 遍历通道与关闭通道Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:v, ok := <-ch如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。实例package mainimport (        "fmt")func fibonacci(n int, c chan int) {        x, y := 0, 1        for i := 0; i < n; i++ {                c <- x                x, y = y, x+y        }        close(c)}func main() {        c := make(chan int, 10)        go fibonacci(cap(c), c)        // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个        // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据        // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不        // 会结束,从而在接收第 11 个数据的时候就阻塞了。        for i := range c {                fmt.Println(i)        }}执行输出结果为:0112358132134
  • [技术干货] go错误处理
    Go 错误处理Go 语言通过内置的错误接口提供了非常简单的错误处理机制。error类型是一个接口类型,这是它的定义:type error interface {    Error() string}我们可以在编码中通过实现 error 接口类型来生成错误信息。函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:func Sqrt(f float64) (float64, error) {    if f < 0 {        return 0, errors.New("math: square root of negative number")    }    // 实现}在下面的例子中,我们在调用Sqrt的时候传递的一个负数,然后就得到了non-nil的error对象,将此对象与nil比较,结果为true,所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:result, err:= Sqrt(-1)if err != nil {   fmt.Println(err)}实例实例package mainimport (    "fmt")// 定义一个 DivideError 结构type DivideError struct {    dividee int    divider int}// 实现 `error` 接口func (de *DivideError) Error() string {    strFormat := `    Cannot proceed, the divider is zero.    dividee: %d    divider: 0`    return fmt.Sprintf(strFormat, de.dividee)}// 定义 `int` 类型除法运算的函数func Divide(varDividee int, varDivider int) (result int, errorMsg string) {    if varDivider == 0 {            dData := DivideError{                    dividee: varDividee,                    divider: varDivider,            }            errorMsg = dData.Error()            return    } else {            return varDividee / varDivider, ""    }}func main() {    // 正常情况    if result, errorMsg := Divide(100, 10); errorMsg == "" {            fmt.Println("100/10 = ", result)    }    // 当除数为零的时候会返回错误信息    if _, errorMsg := Divide(100, 0); errorMsg != "" {            fmt.Println("errorMsg is: ", errorMsg)    }}执行以上程序,输出结果为:100/10 =  10errorMsg is:      Cannot proceed, the divider is zero.    dividee: 100    divider: 0
  • [技术干货] go语言接口
    Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。实例实例/* 定义接口 */type interface_name interface {   method_name1 [return_type]   method_name2 [return_type]   method_name3 [return_type]   ...   method_namen [return_type]}/* 定义结构体 */type struct_name struct {   /* variables */}/* 实现接口方法 */func (struct_name_variable struct_name) method_name1() [return_type] {   /* 方法实现 */}...func (struct_name_variable struct_name) method_namen() [return_type] {   /* 方法实现*/}实例实例package mainimport (    "fmt")type Phone interface {    call()}type NokiaPhone struct {}func (nokiaPhone NokiaPhone) call() {    fmt.Println("I am Nokia, I can call you!")}type IPhone struct {}func (iPhone IPhone) call() {    fmt.Println("I am iPhone, I can call you!")}func main() {    var phone Phone    phone = new(NokiaPhone)    phone.call()    phone = new(IPhone)    phone.call()}在上面的例子中,我们定义了一个接口Phone,接口里面有一个方法call()。然后我们在main函数里面定义了一个Phone类型变量,并分别为之赋值为NokiaPhone和IPhone。然后调用call()方法,输出结果如下:I am Nokia, I can call you!I am iPhone, I can call you!
  • [技术干货] go语言递归函数
    递归,就是在运行的过程中调用自己。语法格式如下:func recursion() {   recursion() /* 函数调用自身 */}func main() {   recursion()}Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。阶乘以下实例通过 Go 语言的递归函数实例阶乘:实例package mainimport "fmt"func Factorial(n uint64)(result uint64) {    if (n > 0) {        result = n * Factorial(n-1)        return result    }    return 1}func main() {      var i int = 15    fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))}以上实例执行输出结果为:15 的阶乘是 1307674368000斐波那契数列以下实例通过 Go 语言的递归函数实现斐波那契数列:实例package mainimport "fmt"func fibonacci(n int) int {  if n < 2 {   return n  }  return fibonacci(n-2) + fibonacci(n-1)}func main() {    var i int    for i = 0; i < 10; i++ {       fmt.Printf("%d\t", fibonacci(i))    }}以上实例执行输出结果为:0    1    1    2    3    5    8    13    21    34
  • [技术干货] go map元集合
    Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。定义 Map可以使用内建函数 make 也可以使用 map 关键字来定义 Map:/* 声明变量,默认 map 是 nil */var map_variable map[key_data_type]value_data_type/* 使用 make 函数 */map_variable := make(map[key_data_type]value_data_type)如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对实例下面实例演示了创建和使用map:实例package mainimport "fmt"func main() {    var countryCapitalMap map[string]string /*创建集合 */    countryCapitalMap = make(map[string]string)    /* map插入key - value对,各个国家对应的首都 */    countryCapitalMap [ "France" ] = "巴黎"    countryCapitalMap [ "Italy" ] = "罗马"    countryCapitalMap [ "Japan" ] = "东京"    countryCapitalMap [ "India " ] = "新德里"    /*使用键输出地图值 */    for country := range countryCapitalMap {        fmt.Println(country, "首都是", countryCapitalMap [country])    }    /*查看元素在集合中是否存在 */    capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */    /*fmt.Println(capital) */    /*fmt.Println(ok) */    if (ok) {        fmt.Println("American 的首都是", capital)    } else {        fmt.Println("American 的首都不存在")    }}以上实例运行结果为:France 首都是 巴黎Italy 首都是 罗马Japan 首都是 东京India  首都是 新德里American 的首都不存在delete() 函数delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:实例package mainimport "fmt"func main() {        /* 创建map */        countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}        fmt.Println("原始地图")        /* 打印地图 */        for country := range countryCapitalMap {                fmt.Println(country, "首都是", countryCapitalMap [ country ])        }        /*删除元素*/ delete(countryCapitalMap, "France")        fmt.Println("法国条目被删除")        fmt.Println("删除元素后地图")        /*打印地图*/        for country := range countryCapitalMap {                fmt.Println(country, "首都是", countryCapitalMap [ country ])        }}以上实例运行结果为:原始地图India 首都是 New delhiFrance 首都是 ParisItaly 首都是 RomeJapan 首都是 Tokyo法国条目被删除删除元素后地图Italy 首都是 RomeJapan 首都是 TokyoIndia 首都是 New delhi
  • [技术干货] go语言范围
    Go 语言范围(Range)Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。实例实例package mainimport "fmt"func main() {    //这是我们使用range去求一个slice的和。使用数组跟这个很类似    nums := []int{2, 3, 4}    sum := 0    for _, num := range nums {        sum += num    }    fmt.Println("sum:", sum)    //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。    for i, num := range nums {        if num == 3 {            fmt.Println("index:", i)        }    }    //range也可以用在map的键值对上。    kvs := map[string]string{"a": "apple", "b": "banana"}    for k, v := range kvs {        fmt.Printf("%s -> %s\n", k, v)    }    //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。    for i, c := range "go" {        fmt.Println(i, c)    }}以上实例运行输出结果为:sum: 9index: 1a -> appleb -> banana0 1031 111