Go part 2 基础语法
关键字、标识符
标识符:
是用户或系统定义的有意义单词组合,或单词与数字组合(具体意义有定义者决定)
标识符以字母下划线开头,大小写敏感,比如:boy, Boy, _boy, _(匿名变量,用来忽略结果)
标识符命名规范:在习惯上,Go语言程序员推荐使用驼峰式命名,当名字有几个单词组成的时优先使用大小写分隔,而不是优先用下划线分隔。因此,在标准库有QuoteRuneToASCII和parseRequestLine这样的函数命名,但是一般不会用quote_rune_to_ASCII和parse_request_line这样的命名。而像ASCII和HTML这样的缩略词则避免使用大小写混合的写法,它们可能被称为htmlEscape、HTMLEscape或escapeHTML,但不会是escapeHtml。
关键字:
是 Go 语言提供的有特殊含义的符号,也叫做“保留字”
系统保留关键字:
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthough | if | range | type |
continue | for | import | return | var |
常量与变量
常量
常量使用 const 修饰,表示是只读的,不能修改
const 只能修饰 boolean,number(int相关,浮点数,complex)和 string 类型
语法:const identifier [type] = value(type 可省略)
优雅写法:
const(
name string = "skier"
age int = 10
salary int = 5000 / 2
// gender boolean = getGender() // const不能从函数中获取
)
常量因为在编译期确定,所以可以用于数组声明:
const size = 4
var arrayA [size]int
变量
声明一个变量:var identifier [type]
// 声明变量并赋值
var a int = 100 // 简写(自动推导类型)
a := 100
优雅写法:
var (
name string = "johny"
age int = 10
)
默认值:
- int 0
- float 0.0,编译默认推导为 float64
- bool false
- string ""
- slice,函数,指针变量默认为 nil (slice 默认为 nil,但打印输出是 [],可使用 == nil 进行判空)
作用域:从定义变量的代码行开始,一直到直接所属的大括号结束为止(全局变量除外)
- 在函数内部声明的变量叫做局部变量,生命周期仅限于函数内部
- 在函数外部声明的变量叫做全局变量,生命周期作用于整个包,如果首字母大写的话,则可以被其它包导入
在编程中,变量在其实现了变量的功能后,作用范围越小,所造成的问题可能性越小,每一个变量代表一个状态,有状态的地方,状态就会被修改,函数的局部变量只会影响一个函数的执行,但全局变量可能会影响所有代码的执行状态,因此限制变量的作用范围对代码的稳定性有很大的帮助
基本数据类型与操作符
数字类型:int8, int16, int32, int64, uint8, uint16, uint32, uint64
bool 类型:ture, false (bool 型无法参与数值运算,也无法与其他类型进行转换。)
浮点类型:float32, float64
字符类型:byte
字符串类型:字符串实现基于 UTF-8 编码
"" 双引号,定义单行字符串
`` 反引号,定义多行字符串(在这种方式下,反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出)
多行字符串一般用于内嵌源码和内嵌数据等
类型转换
格式:type(variable)
var a int = 8
var b int32 = int32(a)
浮点数转换成 int 类型,精度会丢失
var c float32 = math.Pi
fmt.Println(int(c))
int32 转换成 int16,数据会截断
// 初始化一个32位整型值
var a int32 = 1047483647
// 输出变量的十六进制形式和十进制值
fmt.Printf("int32: 0x%x %d\n", a, a) // 将a变量数值转换为十六进制, 发生数值截断
b := int16(a)
// 输出变量的十六进制形式和十进制值
fmt.Printf("int16: 0x%x %d\n", b, b) 结果是:
int32: 0x3e6f54ff 1047483647
int16: 0x54ff 21759
字符串转义符
转移符 | 含 义 |
---|---|
\r | 回车符(返回行首) |
\n | 换行符(直接跳到下一行的同列位置) |
\t | 制表符 |
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
操作符
数字操作符:+, -, *, /, %
比较运算符:>, >=, <, <=, ==, !=
字符串操作
拼接:
var str1 string = "hello"
var str2 string = "world"
var str string = str1 + str2 // var str string = fmt.Sprintf("%s%s", str1, str2)
值类型与引用类型
值类型
本质上是原始类型,变量直接储存值,内存通常在栈中分配,包括 int, float, bool, string 以及数组和 struct(结构体)
对值类型进行操作,一般都会返回一个新创建的值,所以把这些值传递给函数时,其实传递的是一个值的副本
func main() {
name:="张三"
fmt.Println(modify(name))
fmt.Println(name)
} func modify(s string) string{
s=s+s
return s
} //Output
张三张三
张三
以上是一个操作字符串的例子,通过打印的结果,可以看到,本来 name 的值并没有改变,也就是说,我们传递的是一个副本,并且返回一个新创建的字符串
基本类型因为是值的拷贝,并且在对他进行操作的时候,生成的也是新创建的值,所以这些类型在多线程里是安全的,我们不用担心一个线程的修改影响了另外一个线程的数据
引用类型
引用类型与值类型恰恰相反,它的修改可以影响到任何引用到它的变量;变量存储的是地址,这个地址存储最终的值,通常在堆内存上分配,通过 GC 回收,包括 指针,select,map,chan 等
引用类型之所以可以引用,是因为我们创建引用类型的变量,其实是一个标头值,标头值里包含一个指针,指向底层的数据结构,当我们在函数中传递引用类型时,其实传递的是这个标头值的副本,它所指向的底层结构并没有被复制传递,这也是引用类型传递高效的原因。
流程控制
if else 分支判断
if condition1 {
block1
} else if condition2 {
block2
} else {
block3
}
switch case 语句
func main(){
var variabel string = "a"
switch variabel {
case "a", "b":
fmt.Println(variabel)
// fallthrough // 会执行下一个case的语句块
case "c":
fmt.Println(variabel)
default:
fmt.Println("default output")
}
}
case 后边的值可以写多个,是 或 的关系
case 语句块末尾如果加上 fallthrough,会接着执行下一个 case 的语句块
for 循环
for i:=0; i<100 ; i++ {
fmt.Println("hello, world~")
}
死循环(for)
func main(){
for {
fmt.Println("hello, world~")
time.Sleep(time.Second)
}
}
加判断的 for 循环
func main(){
var i int
for i<100 {
fmt.Println(i)
i += 1
}
}
for range 语句
可以使用 for range 遍历数组、切片、字符串、map 及通道(channel)。通过 for range 遍历的返回值有一定的规律:
- 数组、切片、字符串返回索引和值
- map 返回键和值
- 通道(channel)只返回通道内的值
遍历数组:
var arrayA = [3]string{"hammer", "soldier", "mum"} for index, value := range arrayA {
fmt.Println(index, value)
} 运行结果:
0 hammer
1 soldier
2 mum
用匿名标识符忽略 index
var arrayA = [3]string{"hammer", "soldier", "mum"} for _, value := range arrayA {
fmt.Println(value)
} 运行结果:
hammer
soldier
mum
当然,for 循环中也能够支持:break, continue
goto 语句
可以通过标签进行代码间的无条件跳转,goto语句可以快速跳出循环 或 实现同样的逻辑 有一定的帮助:
快速跳出循环:
func main(){
for i:=0; i<=100; i++{
for j:=0; j<=100; j++{
if j == 10 {
// 直接跳转到标签
goto breakHere
}
}
} breakHere:
fmt.Println("hello world~")
} 运行结果:
hello world~
使用 goto 集中处理错误:
err := firstCheckError()
if err != nil {
goto onExit
} err = secondCheckError()
if err != nil {
goto onExit
} fmt.Println("done")
return onExit:
fmt.Println(err)
exitProcess()
函数
Go 语言支持普通函数、匿名函数和闭包
普通函数声明:func 函数名(参数列表) (返回值列表) {函数体}
不支持重载,一个源文件内不能有两个相同名称的函数
函数是一等公民,也是一种类型,可以赋值给变量
函数的传参方式:
- 值传递 (基本数据类型都是值传递)
- 引用传递 (指针,slice,map,chan,interface)
注意:无论是值传递还是引用传递,传递给函数的都是变量的副本,不过,值传递是对值的拷贝,引用传递是地址的拷贝,一般来说,地址拷贝更为高效,而值拷贝取决于拷贝对象的大小,对象越大,则性能越低
返回值命名:
返回值不需要定义,直接使用(命名的返回值变量的默认值为类型的默认值,即数值为 0,字符串为 "",布尔为 false、指针为 nil)
func calc(a int, b int) (c int) {
c = a + b
return c
}
可变长参数:
可变参数变量是一个包含所有参数的切片
func calc(a int, b int, arg... int) {
fmt.Println(arg[0])
}
defer 的用途:
- 延迟调用是在 defer 所在函数结束时进行,函数结束可以是正常返回时,也可以是发生宕机时
- 多个 defer 语句,按先进后出(栈)的顺序执行
- defer 语句中的变量,在 defer 声明时就决定了
- defer 语句正好是在函数退出时执行的语句,所以使用 defer 能非常方便地处理资源释放问题
关闭文件句柄
注意:不能将这一句代码放在第3行空行处,一旦文件打开错误,f将为空,在延迟语句触发时,将触发宕机错误
func read(){
file err := open(filename)
if err != nil{
return
}
defer file.Close()
}
锁资源的释放
func lock(){
mc.Lock()
defer mc.Unlock()
}
数据库连接的释放
func connect(){
conn := openDatabase()
defer conn.Close()
}
调用函数
函数在定义后,可以通过调用的方式,让当前代码跳转到被调用的函数中进行执行。调用前的函数局部变量都会被保存起来不会丢失;被调用的函数结束后,恢复到被调用函数的下一行继续执行代码,之前的局部变量也能继续访问
递归函数
一个函数在内部调用自己,就叫做递归,下面来举两个递归函数的Demo
递归求阶乘
func calc(n int) int {
if n <= 1{
return 1
} return calc(n-1) * n
} func main(){
result := calc(5)
fmt.Println(result)
} 运行结果:
120
斐波拉契数
func fab(n int) int {
if n<=1{
return 1
} return fab(n-1) + fab(n-2) } func main(){
var n int = 6
var fabCount int
for i:=0; i<=n; i++{
fabCount += fab(i)
} fmt.Println(fabCount)
} 运行结果:
33
匿名函数
匿名函数没有函数名,只有函数体,可以直接被当做一种类型赋值给函数类型的变量,匿名函数也往往以变量的方式被传递
匿名函数经常被用于实现回调函数、闭包
定义一个匿名函数:
func(str string){
fmt.Println("hello", str)
}("world") 运行结果:
hello world
也可以将匿名函数赋值给变量:
f := func(str string){
fmt.Println("hello", str)
} f("world")
匿名函数当做参数:
func visit(sliceA []int, f func(int)){
for _, value := range sliceA {
f(value)
}
} func main(){
var sliceA []int = []int{1,2,3,4,5} f := func(a int){
fmt.Print(a)
}
visit(sliceA, f)
} 运行结果:
12345
使用匿名函数实现操作封装:
func main(){
var mapA map[string]func()
mapA = map[string]func(){
"fire": func(){
fmt.Println("chicken fire")
},
"run": func(){
fmt.Println("soldier run")
},
"fly": func(){
fmt.Println("angel fly")
},
}
// fmt.Println(mapA)
// 接收命令行参数,key,默认值,帮助
var skill *string = flag.String("skill", "", "skill type")
flag.Parse() f, err := mapA[*skill]
if err == true {
f()
} else {
fmt.Println("skill not fount")
}
} 运行效果:
$ go run main.go --skill=fly
angel fly
$ go run main.go --skill=run
soldier run
闭包(Closure)
闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境 也不会被释放或者删除,在闭包中可以继续使用这个自由变量(闭包(Closure)在某些编程语言中也被称为 Lambda 表达式)
简单的说:
闭包 = 函数 + 引用环境
同一个函数与不同的引用环境组合,可以形成不同的实例,如图:
实现一个简单的闭包:
func closureFunc(str string) func(){
wapper := func(){
fmt.Println("hello", str)
}
return wapper
} func main(){
f := closureFunc("world~")
// 调用闭包函数
f()
} 运行结果:
hello world~
累加器的实现(闭包的记忆效应)
func accumulate(num int) func() int {
wapper := func() int {
num += 1
return num
}
return wapper
} func main(){
accumulator := accumulate(10)
ret1 := accumulator()
fmt.Println(ret1)
ret2 := accumulator()
fmt.Println(ret2)
} 运行结果:
11
12
go 基本程序结构
// 任何一个代码源文件隶属于一个包
package main //import 关键字,同一个包内的函数可以直接调用,不同包中的函数通过 包名.函数名 的方式调用
import (
"fmt"
"/go_dev/test"
) // 初始化函数
func init(){
fmt.Println("执行初始化操作")
} // main 程序入口
func main(){
fmt.Println("hello, world~")
}
init 函数
每个源文件都可以包含一个 init 函数,会自动被编译器执行
包访问控制规则
- 包内函数名首字母大写表示此函数/变量是可导出的
- 小写表示此函数/变量是私有的,在包外部不能访问
程序执行顺序(栈) *
- 导入其它包,初始化 被导入包 内的全局变量,执行 被导入包 内所有源文件的 init 函数
- 初始化 main 包内全局变量
- 调用 main 包 init 函数
- 执行 main 函数
练习1:写一个函数,对于一个整数n,求出所有两两相加等于n的组合,比如 n=5
package main import (
"fmt"
) func calc(n int){
for i:=0; i<=n; i++ {
num := n - i
fmt.Printf("%d+%d=%d\n", i, num, n)
} } func main(){
calc(5)
} 结果:
0+5=5
1+4=5
2+3=5
3+2=5
4+1=5
5+0=5
练习2:一个程序包含 add 和 main 两个包,add 包中有两个变量 Name 和 age,在 main 中访问 Name 和 age 变量,打印输出
首先在 go_dev 下新建一个目录 example(go_dev 目录在环境变量 GOPATH 下 src 目录,go编译器根据系统路径会找到 go_dev 目录下的包)
add包下写 add.go
package add var Name string = "skier"
var age int = 19
main包下写 main.go
package main import (
"go_dev/example/add"
// a "go_dev/example/add" // 包别名的应用
"fmt"
) func main(){
fmt.Println(add.Name)
fmt.Println(add.age) // 不能访问
}
作业
1.判断101~200之间有多少个素数,并输出打印
package main import (
"fmt"
) func main(){
for i:=101; i<=200; i++{
var flag bool = true
for j:=2; j<i; j++{
if (i % j == 0) {
flag = false
}
}
if (flag == true){
fmt.Println("素数i:", i)
}
}
}
第一题
2.打印出101~999中所有的水仙花数,所谓水仙花数是指一个三位数,其各位数字的立方和等于该数本身,例如153是一个水仙花数,1*1*1 + 5*5*5 + 3*3*3 = 153
package main import (
"fmt"
) func narcissisticNum(num int) bool {
var g int = num % 100 % 10
var s int = num % 100 / 10
var b int = num / 100
//fmt.Println(g, s, b)
var cube int = g*g*g + s*s*s + b*b*b
return num == cube
} func main(){
for i:=100; i<=999; i++{
result := narcissisticNum(i)
if result == true {
fmt.Println("水仙花数是:", i)
}
}
}
第二题
3.对于一个数n,求n的阶乘之和,即:1! + 2! + ... + n!
package main import (
"fmt"
) func summation(n int) int {
var result int
for i:=1; i<=n; i++ {
var tmpe int = 1
for j:=1; j<=i; j++{
tmpe *= j
}
result += tmpe
}
return result
} func main(){
var num int = 5
var result int = summation(num)
fmt.Println("sum result:", result)
}
第三题
4.在终端打印 9*9 乘法表
package main import(
"fmt"
) func multiplication(){
for column:=1; column<=9; column++{
for row:=1; row<=column; row++{
fmt.Printf("%d*%d=%d\t", column, row, column*row)
}
fmt.Println()
}
} func main(){
multiplication()
}
第四题
5.一个数如果恰好等于它的因子之和,这个数就称之为“完数”,例如:1+2+3=6,在终端输出1000以内的所有完数
package main import(
"fmt"
) func showPerfect(n int){
for i:=1; i<n; i++{
var sum int for j:=1; j<i; j++{
if i % j == 0{
sum += j
}
} if sum == i{
fmt.Printf("完数:%d \n", i)
}
}
} func main(){
const num int = 1000
showPerfect(num)
}
第五题
6.输入一个字符串,判断其是否为“回文”,回文字符串是指从左到右读,与从右到左读是完全相同的字符串
package main import (
"fmt"
) func isReverse(str string) bool {
// 转为字符
char := []rune(str) var length int = len(char) for i:=0; i<=length / 2 - 1; i++{
if char[i] != char[length-1-i]{
return false
}
} return true
} func main(){
var input string
fmt.Scanf("%s", &input)
result := isReverse(input)
fmt.Println("result:", result)
}
第六题
7.输出一行字符串,分别统计其中英文字母,空格,数字和其它符号的个数
package main import (
"fmt"
"bufio"
"os"
) func count(str string) (wordCount, spaceCount, numCount, otherCount int) {
chars := []rune(str)
for _, value := range chars{
switch {
case value >= 'a' && value <= 'z':
wordCount += 1
case value >= 'A' && value <= 'Z':
wordCount += 1
case value == ' ':
spaceCount += 1
case value >= '0' && value <= '9':
numCount += 1
default:
otherCount += 1
}
}
return
} func main(){
reader := bufio.NewReader(os.Stdin)
// 读取一行的内容
result, _, error := reader.ReadLine() if error == nil{
wordCount, spaceCount, numCount, otherCount := count(string(result))
fmt.Printf("wordCount:%d\nspaceCount:%d\nnumCount:%d\notherCount:%d\n", wordCount, spaceCount, numCount, otherCount)
}
}
第七题
8.计算两个大数相加的和,这两个大数会超过 int64 的表示范围
Go part 2 基础语法的更多相关文章
- Swift与C#的基础语法比较
背景: 这两天不小心看了一下Swift的基础语法,感觉既然看了,还是写一下笔记,留个痕迹~ 总体而言,感觉Swift是一种前后端多种语言混合的产物~~~ 做为一名.NET阵营人士,少少多多总喜欢通过对 ...
- iOS-----正则表达式的基础语法
正则表达式简单语法总结 一.什么是正则表达式 从概念上来说,正则表达式也是一门小巧而精炼的语言,它可以用来简化检索特定的字符串,替换特定字符等功能,有许多开发语言工具,都内嵌支持正则表达式.那么一个正 ...
- python之最强王者(2)——python基础语法
背景介绍:由于本人一直做java开发,也是从txt开始写hello,world,使用javac命令编译,一直到使用myeclipse,其中的道理和辛酸都懂(请容许我擦干眼角的泪水),所以对于pytho ...
- emmet 系列(1)基础语法
emmet 系列(1)基础语法 emmet 是一个能显著提升开发html和css开发效率的web开发者工具 emmet基本上目前已知的编辑器都有相应的插件,各个编辑器的emmet插件的下载地址:点我下 ...
- Scala基础语法 (一)
如果你之前是一名 Java 程序员,并了解 Java 语言的基础知识,那么你能很快学会 Scala 的基础语法. Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的. 我 ...
- Java基础语法
java基础学习总结——基础语法1 一.标识符
- javascript中正则表达式的基础语法
× 目录 [1]定义 [2]特点 [3]元字符[4]转义字符[5]字符组[6]量词[7]括号[8]选择[9]断言[10]模式[11]优先级[12]局限性 前面的话 正则表达式在人们的印象中可能是一堆无 ...
- Swift基础语法学习总结(转)
Swift基础语法学习总结 1.基础 1.1) swift还是使用// 和/* */ 来注释,并且/* */允许多行注释. 1.2) swift使用print和println打印,它的传参是一个泛型 ...
- 黑马程序员——OC语言基础语法 面向对象的思想
Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结)(一)基础语法 1)关键字 @interface.@implementati ...
- 【OC基础语法考试】
OC基础语法已经全部学完,但是这些知识只是最基础的,还有很多高级知识,这个可能需要后面慢慢的去学习才能体会到.接下来我会总结前面的OC基础语法,如果大家发现有什么不正确的地方,请指正,小弟是新生,多请 ...
随机推荐
- 【422】Insert often-used pieces of text in gedit
Ref: Snippets Enable Snippets Plugin Browse Snippets Using Snippets To insert a nippet into your cur ...
- PAT 甲级 1051 Pop Sequence (25 分)(模拟栈,较简单)
1051 Pop Sequence (25 分) Given a stack which can keep M numbers at most. Push N numbers in the ord ...
- Apache三种工作模式详解
Apache HTTP服务器被设计为一个强大的.灵活的能够在多种平台以及不同环境下工作的服务器.这种模块化的设计就叫做“多进程处理模块”(Multi-Processing Module,MPM),也叫 ...
- 【ARTS】01_33_左耳听风-201900624~201900630
ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...
- charles 查找
本文参考:charles 查找 查找设置 界面如上图,也没啥好说的: 这个还是非常常用的:需要了解 界面如上图,也没啥好说的: 这个还是非常常用的:需要了解 查找需要了解的关键字是: 查找的内容 正则 ...
- 前端添加视频流rtmp格式
要求:rtmp格式, 在线直播 url地址 效果: 代码:初次打开时间较长, <!DOCTYPE html> <html> <head> <script ty ...
- 删除Excel表格中一堆英文中的汉字
昨天需要处理一个Excel文件,删除一堆英文里的汉字,开始搜了下方法,没找到,然后手动一个多小时,弄了一半吧也就,结果电脑卡了,忘了保存,就白做了...不知道为啥这次没有自动保存,所以,重要的事说三遍 ...
- table列表全选
<table><tr><td><input type="checkbox" /></td><td></ ...
- python从hello world开始 - python基础入门(3)
万丈高楼平地起,编程亦如此.改变世界是结果,坚持努力学习改bug是过程,hello world是开始,所有语言均是如此. 一.使用pycharm创建第一个hello world 项目 1.Create ...
- Clustering and Exploring Search Results using Timeline Constructions (paper2)
作者:Omar Alonso 会议:CIKM 2009 摘要: 截至目前(2009),通过提取文档中内嵌的时间信息来展现和聚类,这方面的工作并不多. 在这篇文章中,我们将提出一个“小插件”增添到现有的 ...