Go+

目录

一、前言

去年7月,七牛云首次提出Go+,填补了国人开发者在数据科学领域的空白。经过一年多时间的打磨,Go+1.0已于10月15日面世。目前,Go+1.0已经能够为工程使用,而且语言的使用门槛做了进一步的降低,更接近自然语言,1.0的门槛甚至比Python更低,使得Go+ 更适合STEM教育的场景。

宣传的这么厉害我也来学习一下,作为没有使用过Golang的我看看是不是真的好上手。

百度先查了下 go+,官网https://goplus.org/ ,GitHub项目地址 https://github.com/goplus/gop,用我蹩脚的英语翻译下,go+是一个未来可能非常火的简单易学的开发语言,可以进行工程、数据科学方面工作。

注意:go+ 完全兼容 golang 的,那学习go+的理由应该就只有 语法简单的吧。

我在学习的过程中遇到了几个问题,相信想了解Go+的大多都是没有使用过Go的,不妨也从下面的问题来学习如何使用go+,可以运行 hello, world !后再熟悉语法。

go+ 怎么安装?
go+ 依赖库怎么安装?
项目内文件怎么引用?
如何执行go+程序?

二、前期准备


go+的运行是基于go开发环境的,安装go+前 我们需要安装go的开发环境,我使用的是Windows操作系统。

1. 安装 Git


go的库管理是依赖git的,所以我们首先安装git工具。

在 Windows 上安装 Git 也有几种安装方法。 官方版本可以在 Git 官方网站下载。 打开 https://git-scm.com/download/win,下载会自动开始。

2. 安装 Go


下载地址 https://golang.google.cn/dl/, 我下载的是 go 1.17 版本(go1.17.3.windows-amd64.msi)
windows下安装完成是直接添加了环境变量的,安装完成可以在命令行查看go版本:

在这里插入图片描述

在这里插入图片描述

3. 安装 Go Idea


我是下载了GoLand 2021.2.4 地址 https://www.jetbrains.com/go/download/download-thanks.html ;
IntelliJ IDEA 也有 go 和 goplus 的2020.03版的插件可以安装;
PyCharm 只有 2016-2017版的插件,新版都不支持了。

4. Go执行hello world !

go run hello.go

# hello.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
    fmt.Println("Hello, 不太灵光的程序员!")
}

 在这里插入图片描述

5. 安装 Go+

当前goplus最新版本为v1.0.14,go get会调用git命令从代理网址拉取代码

go get github.com/goplus/gop@v1.0.14
# 查看工作去地址
$ go env GOPATH
C:\Users\Lenovo\go
$ cd %GOPATH%\pkg\mod\github.com\goplus\gop@v1.0.14\cmd
# 编译 并将 goplus tools 移动到gobin目录,方便调用 build all Go+ tools
$ go install -v  .\...

6. Go+ 执行hello world !

gop run hello.gop

println("Hello, World!")
println("Hello, 不太灵光的程序员!")

在这里插入图片描述

三、Go+的一些特性

1. 注释

Go/Go+注释

// 单行注释
/*
多行注释
*/

Python注释

# 单行注释
"""
多行注释
"""

2. 数据类型

  • 布尔型:布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。

  • 整型:整型 int 和浮点型 float32、float64,Go +语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。

  • 字符串:字符串就是一串固定长度的字符连接起来的字符序列。

Go+示例

// 布尔值
println true && false
println true || false
println !true

// 数值
println("1+1 =", 1+1)
println("7.0/3.0 =", 7.0/3.0)

// 字符串
println "Go"+"Plus"

3. 基础变量类型

变量可以通过变量名访问。Go+ 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

  • 指定变量类型,如果没有初始化,则变量默认为空值。
    • 数值类型(包括complex64/128)为 0
    • 布尔类型为 false
    • 字符串为 “”(空字符串)
  • 根据值自行判定变量类型。
    • 如果这个值在前面被 var 声明过的话会触发异常

Go+示例

// 指定变量类型,如果没有初始化,则变量默认为空值
var phone string = "11111"
println phone

// 布尔型
var bo bool
// 字符型
var by byte
// 无符号整型
var i int
// 无符号整型
var ui uint
// 浮点型
var f float64
// 字符穿
var s string
// 格式化输出
printf("%v %v %v %v %v %q\n", bo, by, i, ui, f, s)

// 根据值自行判定变量类型
phone1 := "11111"
println phone1

4. 有理数


我们将有理数作为原生 Go+ 类型引入。
我们使用后缀r来表示有理文字。例如, (1r << 200) 表示一个 big int, 其值等于 2 200。4/5r 表示有理常数 4/5。
<<运算符,将一个运算对象的各二进制位全部左移若干位

println 1r << 200
println 4/5r  // 4/5
println 4/5  // 0

5. 容器类型

5.1. 集合

Go 示例

package main

import "fmt"

func main() {
    // 集合
    z := map[string]string{"m1": "1"}
    // 集合插入元素
    z["name"] = "不太灵光的程序员"
    z["age"] = "18"
    fmt.Println(z["name"])
    // 集合删除元素
    delete(z, "name")
    fmt.Println(z)
}

Go+ 示例
go+ 语法,简化使用 make 方法

x := {"m1": 1}
y := {"m1": 1, "m2": "go+!"}
z := {}

// 集合插入元素
z["name"] = "不太灵光的程序员"
z["age"] = 18
println z["name"]
// 集合删除元素
delete(z, "name")
println z
println x, y, z

Python 示例

x = dict(m1=1)
y = {"m1": 1, "m2": "go+!"}
z = {}

# 插入元素
z.update({"name": "不太灵光的程序员"})
z["age"] = 18
# 删除元素
z.pop("name")
print(x, y, z)

# 遍历字典
for k in z:
    print(z[k])

# 字典推导式
print({k: v for k, v in z.items()})

# 判断key是否存在
print("name" in z)

5.2. 切片

Go 示例

package main

import "fmt"

func main() {
    x := []int{0,1,2,3,4,5}
    // 取索引3
    fmt.Println(x[3])
    // 取索引1-4不包含4
    fmt.Println(x[1:4])
    // 取索引1到最后
    fmt.Println(x[1:])
    // 同时添加多个元素
    x = append(x, 2,3,4)
    fmt.Println(x)

    // 切片遍历
    for i:=0; i < len(x); i++ {
        fmt.Println(x[i])
    }
}

Go+ 示例

x := [0, 1, 2, 3, 4] // []float64
y := [1+2i, "xsw"] // []interface{}
z := []

println x, y, z

// 取索引3
println x[3]
// 取索引1-4不包含4
println x[1:4]
// 取索引1到最后
println x[1:]
// 取索引0到倒数1个索引 不支持
// 切片反转  不支持
// 同时添加多个元素
x = append(x, 2,3,4)
println(x)

Python 示例

# 初始化列表
x = [0, 1, 2, 3, 4, 5]
# 取索引3
print(x[3])
# 取索引1-4不包含4
print(x[1:4])
# 取索引1到最后
print(x[1:])
# 取索引0到倒数1个索引
print(x[:-1])
# 切片反转
print(x[::-1])
# 同时添加多个元素
x.extend([2, 3, 4])
print(x)

# 插入元素
x.append(99)

# 删除元素
x.pop()
x.remove(4)

# 遍历字典
for v in x:
    print(v)

for i in range(len(x)):
    print(x[i])

# 推导式
print({v*v for v in x})

# 判断key是否存在
print(1 in x)

6. 常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
显式类型定义: const b string = “abc”
隐式类型定义: const b = “abc”

Go 示例

package main
import (
    "fmt"
)
func main() {
	const WIDTH, LENGTH int = 5, 10
	area := LENGTH * WIDTH
	println area
	// 修改常量会抛出异常  VarRef *types.Const
	// WIDTH += 1
}

Go+ 示例

const WIDTH, LENGTH int = 5, 10
area := LENGTH * WIDTH
println area
// 修改常量会抛出异常  VarRef *types.Const
// WIDTH += 1

7. 运算符


算术运算符:加+、减-、乘*、除/、求余%、自增++、自减-
关系运算符:==、!=、>、<、>=、<=、
逻辑运算符:%%与、||或、!非
位运算符:%与、|或、^异或、<<左移、>>右移
赋值运算符:赋值运算符比较多 运算法都可以直接赋值,例如 += 相加后赋值
其他运算符:& 取地址符、* 指针变量


8. 语言条件语句

条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句。

Go 示例

package main
import (
    "fmt"
)
func main() {
    // if ... else
    const WIDTH, LENGTH int = 5, 10
    if WIDTH == LENGTH {
      fmt.Println("正方形")
    }else if WIDTH < LENGTH {
      fmt.Println("长方形1")
    }else{
      fmt.Println("长方形2")
    }

    // switch
    var grade string
    var marks int = 90

    switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 60,70 : grade = "C"
      default: grade = "D"
    }
    fmt.Println(marks, grade)
}

Go+ 示例

// if ... else
const WIDTH, LENGTH int = 5, 10
if WIDTH == LENGTH {
  println "正方形"
}else if WIDTH < LENGTH {
  println "长方形1"
}else{
  println "长方形2"
}

// switch
var grade string
var marks int = 90

switch marks {
  case 90: grade = "A"
  case 80: grade = "B"
  case 60,70 : grade = "C"
  default: grade = "D"
}
println marks, grade

Python 示例

# if ... else
WIDTH, LENGTH = 5, 10
if WIDTH == LENGTH:
    print("正方形")
elif WIDTH < LENGTH:
    print("长方形1")
else:
    print("长方形2") 

9. Lambda表达式

Go语言中的匿名函数 指的是不需要有名字的函数,通常定义在另外一个函数内部

Go 示例

package main
import (
    "fmt"
)
func main() {
    Sum := func(x, l int) (int) { return x + x }
    // 变量Sum作为匿名函数来使用,求和
    fmt.Println(Sum(50,10))
    return
}

Go+ 示例

// 并没看懂怎么用  触发异常了 undefined: Sum
Sum x, y => x + y
println(Sum(50,10))

Python 示例

Sum = lambda x, y: x + y
print(Sum(10, 2))
四、运行一个简单的Grpc


前面我们成功的安装了Go+运行环境,下面我们通过运行一个简单的Grpc来了解下如何进行包的引用。

1. 依赖包安装


对于依赖库的引用 Go+ 已经做了很多优化让代码写起来更简单,就像前面执行打印时Go需要import "fmt"才可以调用,Go+可以直接调用println函数。但是除了基础外在日常开发中还是需要用到很多外部依赖库,还更便捷的进行开发工作,下面来了解下Go包管理工具go.mod。

2. 什么是go.mod?


Go.mod是Golang1.11版本新引入的官方包管理工具用于解决之前没有地方记录依赖包具体版本的问题,方便依赖包的管理。

Go.mod其实就是一个Modules,关于Modules的官方定义为:

Modules是相关Go包的集合,是源代码交换和版本控制的单元。go命令直接支持使用Modules,包括记录和解析对其他模块的依赖性。Modules替换旧的基于GOPATH的方法,来指定使用哪些源文件。

Modules和传统的GOPATH不同,不需要包含例如src,bin这样的子目录,一个源代码目录甚至是空目录都可以作为Modules,只要其中包含有go.mod文件。

3. 设置代理


执行go get 或 go install 安装依赖时可能会出现地址无法访问的情况,可以设置代理地址,在win10任务栏左下角的win标志右键,以管理员身份打开powershell

$env:GOPROXY = "https://goproxy.io"

## 如果还是无法访问,还需要设置下外网的代理

set http_proxy=127.0.0.1:19180
set https_proxy=127.0.0.1:19180

4. 设置自动获取依赖


go env -w GO111MODULE=auto

  • GO111MODULE有三个值:off, on和auto(默认值)。
  • GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。
  • GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。
  • GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:
  • 当前目录在GOPATH/src之外且该目录包含go.mod文件当前文件在包含go.mod文件的目录下面。

5. 初始化模块


测试项目目录结构如下图所示:

在这里插入图片描述

在根目录执行go mod {包名称} 命令后会生成一个go.mod文件,后面这个文件会记录项目使用的包和包版本,有点像Python中的requirements.txt文件。

go.mod 文件
module hello

go 1.17

注意:子目录里是不需要init的,所有的子目录里的依赖都会组织在根目录的go.mod文件里

执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载

go.mod 文件
module hello

go 1.17

require (
	github.com/golang/protobuf v1.5.2 // indirect
	github.com/goplus/gop v1.0.14 // indirect
	golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
	golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
	golang.org/x/text v0.3.7 // indirect
	google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
	google.golang.org/grpc v1.42.0 // indirect
	google.golang.org/protobuf v1.27.1 // indirect
)

6. 引用项目文件

helloworld.proto文件
syntax = "proto3";

package hello;

option go_package = "/protos";

// 定义一个服务
service Greeter {
  // 定义一个函数
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 定义函数的输入
message HelloRequest {
  string name = 1;
}

// 定义函数的输出
message HelloReply {
  string message = 1;
}

在protos目录下是我们生成的grpc协议文件,了解proto 。首先我们需要编辑一个标准的proto协议文件,然后执行下面的编译命令生成go的协议文件。

protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/helloworld.proto

# 我们的示例
protoc.exe --go_out=plugins=grpc:. helloworld.proto

在这里插入图片描述

生成的helloworld.pb.go文件是grpc客户端和grpc服务端都需要引用的文件,用来指定一个标准的通信规则,怎么来引用这个文件呢?我们在前面初始化了我们当前项目的模块名称是 hello,所以我们在import中添加"hello/protos"来引用该文件。

7. grpc 客户端示例

client.go文件

package main

import (
    "log"
    "os"

    "golang.org/x/net/context"
    "google.golang.org/grpc"

    pb "hello/protos"
)

const (
    address     = "localhost:8888"
    defaultName = "不太灵光的程序员"
)

func main() {
    // Set up a connection to the server.
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}

8. grpc 服务端示例

server.go文件

package main

import (
    "log"
    "net"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"

    pb "hello/protos"
)

const (
    port = ":8888"
)

// GreeterServerImplementation is used to implement helloworld.GreeterServer.
type GreeterServerImplementation struct{}

// SayHello implements helloworld.GreeterServer
func (s *GreeterServerImplementation) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &GreeterServerImplementation{})
    // Register reflection service on gRPC server.
    reflection.Register(s)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

9. 运行结果

# 运行grpc服务端
go run server.go
# 运行grpc客户端
go run client.go

在这里插入图片描述

代码转换后 无法直接运行  Go+ 的grpc服务,😥

未完待续 ...

后面我会尝试使用Go+编写 Grpc服务,希望可以也正常运行!