Go+快速开始指南
Go+快速开始
Go+编程语言是为工程、STEM教育和数据科学设计的。
- 对于工程:用儿童能掌握的最简单的语言工作。
- 对于STEM教育:学习一门可以在未来工作中使用的工程语言。
- 对于数据科学:用同一种语言与工程师交流。
安装方法
现在,我们建议您从源代码安装Go+。
注意:需要go1.18或更高版本
1 | git clone https://github.com/goplus/gop.git |
实际上,all.bash
和 all.bat
将会在底层用 go run cmd/make.go
.
在Go+ playground里运行
如果你不想安装Go+,你可以在Go+ playground中编写Go+程序。这是体验Go+的最快方式。
- Go+ playground based on Docker: https://play.goplus.org/
- Go+ playground based on GopherJS: https://jsplay.goplus.org/
你可以和你的朋友分享你的Go+代码。这是我的“Hello world”程序:
你好,世界
1 | println "Hello world" |
将这段代码保存到一个名为“hello.gop”的文件中。现在执行:’ gop run hello.gop ‘。
恭喜你——你刚刚编写并执行了你的第一个Go+程序!
你可以用gop build hello.gop
编译一个不需要执行的程序。
有关所有支持的命令,请参阅’ gop help ‘。
println
是为数不多的内置函数之一。它将传递给它的值打印到标准输出。
详见https://tutorial.goplus.org/hello-world。
运行包含多个文件的项目文件夹
假设您有一个包含几个.gop文件的文件夹,并且您想要将它们全部编译成一个程序。只要做:gop run .
。
传递参数也可以,所以你可以这样做:
gop run . --yourparams some_other_stuff
.
然后你的程序可以像这样使用CLI参数:
1 | import "os" |
注释
1 | # This is a single line comment. |
变量
1 | name := "Bob" |
变量用:=
声明和初始化。
变量的类型是从右边的值推断出来的。
要选择不同的类型,请使用类型转换:
表达式’ T(v) ‘将值’ v ‘转换为“T”型。
初始化vs赋值
请注意’:= ‘和’ = ‘之间的(重要)区别。
‘:= ‘用于声明和初始化,’ = ‘用于赋值。
1 | age = 21 |
这段代码无法编译,因为没有声明变量“age”。所有变量都需要在Go+中声明。
1 | age := 21 |
可以在一行中修改多个变量的值。
通过这种方式,可以在没有中间变量的情况下交换它们的值。
1 | a, b := 0, 1 |
Go+类型
基本数据类型
1 | bool |
字符串
1 | name := "Bob" |
字符串值是不可变的。你不能改变元素:
1 | s := "hello 🌎" |
请注意,对字符串进行索引将生成一个’ byte ‘,而不是’ rune ‘或另一个’ string ‘。
字符串可以很容易地转换为整数:
1 | s := "12" |
字符串操作符
1 | name := "Bob" |
Go+中的所有操作符两边必须具有相同类型的值。不能连接一个整型转换为字符串:
1 | age := 10 |
我们也可以将’ age ‘转换为’ string ‘:
1 | age := 10 |
Runes
‘ rune’表示单个Unicode字符,是’ int32 ‘的别名。
1 | rocket := '🚀' |
Numbers
1 | a := 123 |
这将把123的值赋给’ a ‘。默认情况下’ a ‘的类型为’ int ‘。
您还可以使用十六进制,二进制或八进制表示法来表示整数字面值:
1 | a := 0x7B |
所有这些都会被赋相同的值123。它们都有类型’ int ‘,不管你用什么符号。
Go+还支持用’ _ ‘作为分隔符书写数字:
1 | num := 1_000_000 // same as 1000000 |
如果你想要一个不同类型的整数,你可以使用强制转换:
1 | a := int64(123) |
赋值浮点数的工作方式相同:
1 | f1 := 1.0 |
如果不显式指定类型,默认情况下float字面值将具有’ float64 ‘类型。
Float字面值也可以声明为10的幂:
1 | f0 := 42e1 // 420 |
Go+内置了对有理数的支持:
1 | a := 1r << 200 // suffix `r` means `rational` |
你可以将bool类型转换为数字类型(这在Go中不支持):
1 | println int(true) // 1 |
切片
切片是具有相同类型的数据元素的集合。切片字面量是用方括号括起来的表达式列表。单个元素可以是使用index表达式访问。索引从“0”开始:
1 | nums := [1, 2, 3] |
自动推断切片文字的类型。
1 | a := [1, 2, 3] // []int |
强制转换切片片字面量也有效。
1 | a := []float64([1, 2, 3]) // []float64 |
集合
1 | a := {"Hello": 1, "xsw": 3} // map[string]int |
如果没有找到键,默认返回零值:
1 | a := {"Hello": 1, "xsw": 3} |
您还可以检查是否存在一个键,并获取它的值。
1 | a := {"Hello": 1, "xsw": 3} |
模块导入
模块可以使用’ import ‘关键字导入:
1 | import "strings" |
模块导入别名
任何导入的模块名都可以使用别名:
1 | import strop "strings" |
表达式和语句
If..else
在Go+中,’ if ‘语句非常简单,与大多数其他语言类似。
与其他类c语言不同,在条件周围没有括号,并且总是需要大括号。
1 | a := 10 |
For循环
Go+只有一个循环关键字:’ for ‘,有几种形式。
for
/<-
这是最常见的形式。您可以将它与切片、映射、数字范围或自定义迭代器一起使用。
Slice for
‘ for value < - arr ‘形式用于遍历切片的元素。
1 | numbers := [1, 3, 5, 7, 11, 13, 17] |
如果需要索引,可以使用另一种形式’ for index, value < - arr ‘
1 | names := ["Sam", "Peter"] |
Map for
1 | m := {"one": 1, "two": 2} |
Range for
你可以在for循环中使用range expression
(start:end:step
)。
1 | for i <- :5 { |
for
/<-
/if
所有for
/<-
形式的循环都可以有一个可选的’ if ‘条件。
1 | numbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
Condition for
1 | sum := 0 |
这种形式的循环类似于其他语言中的“while”循环。
一旦布尔条件求值为false,循环将停止迭代。
同样,条件周围没有括号,并且总是需要大括号。
C for
1 | for i := 0; i < 10; i += 2 { |
最后,还有传统C风格的for循环。它比“while”形式更安全
因为使用后者很容易忘记更新计数器和get
陷入无限循环。
Bare for
1 | for { |
可以省略该条件,从而导致无限循环。你可以使用’ break ‘或’ return ‘来结束循环。
错误处理
我们在Go+中重新定义了错误处理规范。我们称它们为“ErrWrap表达式”:
1 | expr! // panic if err |
如何使用它们?下面是一个例子:
1 | import ( |
The output of this example is:
1 | add("100", "23"): 123 |
与相应的Go代码相比,它更清晰,更具可读性。
最有趣的是,返回错误包含了整个错误堆栈。当我们遇到错误时,很容易找到根本原因。
这些“ErrWrap表达式”是如何工作的?有关详细信息,请参阅错误处理
函数
1 | func add(x int, y int) int { |
返回多个值
1 | func foo() (int, int) { |
可变参数
1 | func sum(a ...int) int { |
输出参数可以有名称。
1 | func sum(a ...int) (total int) { |
高阶函数
函数也可以是参数。
1 | func square(x float64) float64 { |
Lambda表达式
你也可以使用’ lambda表达式’来定义一个匿名函数。
1 | func transform(a []float64, f func(float64) float64) []float64 { |
结构体
自定义的迭代器
For range of UDT
1 | type Foo struct { |
注意:你不能在udt.Gop_Enum(callback)的范围内使用break/continue或return语句
For range of UDT2
1 | type FooIter struct { |
推断结构类型
1 | type Config struct { |
这里’ foo {Dir: “/foo/bar”, Level: 1} ‘等价于’ foo(&Config{Dir: “/foo/bar”, Level: 1}) ‘。然而,你不能将’ foo(&Config{“/foo/bar”, 1}) ‘替换为’ foo {“/foo/bar”, 1} ‘,因为将’ {“/foo/bar”, 1} ‘视为结构字面值会令人困惑。
您还可以在返回语句中省略结构类型。例如:
1 | type Result struct { |
重载操作符
1 | import "math/big" |
自动属性
Let’s see an example written in Go+:
1 | import "gop/ast/goptest" |
在许多语言中,有一个名为” property “的概念,它有” get “和” set “方法。
假设我们有’ get property ‘,上面的例子将是:
1 | import "gop/ast/goptest" |
在Go+中,我们引入了一个名为“自动属性”的概念。它是一个“get属性”,但是是自动实现的。如果我们有一个名为’ Bar() ‘的方法,那么我们将同时有一个名为’ Bar ‘的’ get属性’。
Go/Go+ 混合程序设计
这是一个展示如何在同一个包中混合Go/Go+代码的示例。
在这个例子中,我们有一个名为’ a.go ‘的Go源文件:
1 | package main |
我们有一个Go+源文件名为’ b.gop ‘:
1 | func sayMix() { |
你可以看到Go调用名为’ sayMix ‘的Go+函数,Go+调用名为’ p ‘的Go函数。正如你在Go编程中习惯的那样,这种循环引用是允许的。
运行’ gop Run . ‘来查看这个示例的输出:
1 | Mix Go and Go+ |
在监视模式下运行Go+
‘ gop ‘命令可以在监视模式下运行,以便每次更改Go+文件时将其转换为Go文件:
1 | gop watch [-gentest] [dir] |
默认情况下,’ gop watch ‘不会转换测试文件(通常以’ _test.gop ‘结尾)。你可以指定’ -gentest ‘标志来强制转换所有Go+文件。
从Go+调用C
- ‘ gop c ‘命令(相当于独立的’ c2go ‘命令)可用于将c项目转换为Go项目。
- ‘ import “C” ‘和’ import “C/xxx” ‘用于导入c2go转换的C项目。其中“import”C是“import”C/github.com/goplus/libc”的缩写。
- ‘ C”xxx” ‘语法表示C风格的字符串常量。
Here is an example to show how Go+ interacts with C.
1 | import "C" |
在这个例子中,我们调用两个C标准函数’ printf ‘和’ fprintf ‘,传递一个C变量’ stderr ‘和两个C字符串,形式为’ C”xxx” ‘ (Go+语法表示C风格字符串)。
运行’ gop run . ‘来查看这个示例的输出:
1 | Hello, c2go! |
当然,目前Go+对C语言的支持只是一个预览版本,还没有到实际工程中可用的程度。就libc而言,目前的移民进度只有5%左右,这只是一个开始。
在即将到来的Go+ v1.2版本计划中,完全支持C语言被列为首要任务。当然,对Go和Go模板的支持也在计划之中,这对Go/Go+混合项目来说是一个至关重要的功能增强。
数据处理
有理数
我们引入有理数作为原始Go+类型。我们使用后缀“r”来表示有理字面值。例如, 1r << 200
表示一个大int,它的值等于 2200.
1 | a := 1r << 200 |
默认情况下,’ 1r ‘的类型为’ bigint ‘
4/5r表示有理常数4/5。
它的类型是’ bigrat ‘。
1 | a := 4/5r |
转换有理数的工作方式类似于其他基本数据类型:
列表推导式
1 | a := [x*x for x <- [1, 3, 5, 7, 11]] |
从集合中选择数据
1 | type student struct { |
检查集合中是否存在数据
1 | type student struct { |
Unix shebang
现在可以使用Go+程序作为shell脚本。例如:
1 | #!/usr/bin/env -S gop run |
与Go的兼容性
所有Go的特性都将被支持.
所有Go包(即使这些包使用’ cgo ‘)都可以通过Go+.导入
1 | import ( |
所有Go+包也可以导入到Go程序中。你需要做的就是使用’ gop ‘命令而不是’ go ‘
首先,让我们创建一个名为“14-Using-goplus-in-Go”的目录。
然后在其中编写一个名为foo的Go+包:
1 | package foo |
然后在Go包中使用它14-Using-goplus-in-Go/gomain
1 | package main |
如何构建这个示例?你可以用:
1 | gop install -v ./... |
字节码 vs Go代码
当我们使用’ gop ‘命令时,它会生成Go代码将Go+包转换为Go包。
1 | gop run # Run a Go+ program |
当我们使用’ igop ‘命令时,它会生成字节码来执行。
1 | igop # Run a Go+ program |
在字节码模式下,Go+不支持’ cgo ‘。然而,在Go代码生成模式下,Go+完全支持’ cgo ‘。