环境搭建
windows环境搭建
参考之前博客:Go语言入门:从零开始的快速指南
Mac环境搭建
安装GO
使用 Homebrew 安装 Go
Homebrew 是 macOS 上常用的包管理工具,使用 Homebrew 安装 Go 非常简单。
-
打开终端(Terminal)。
-
确保你已经安装了 Homebrew。如果没有安装,可以使用以下命令进行安装:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
使用 Homebrew 安装 Go:
brew install go
安装完成后,Go 的执行文件和相关工具将会被安装到
/usr/local/bin/
中,默认版本是最新稳定版。
手动下载安装 Go
如果你不使用 Homebrew,也可以手动从官网下载安装:
- 访问 Go 官方下载页面。
- 下载适用于 macOS 的安装包(通常是
.pkg
文件)。 - 下载完成后,双击安装包并按照提示进行安装。
安装成功后,Go 会被安装到 /usr/local/go
目录。
配置环境变量
Go 的安装目录 /usr/local/go
包含了 Go 的所有工具和命令行工具。为了让系统知道 Go 的位置,我们需要配置 Go 的 GOPATH
和 GOROOT
环境变量。
配置环境变量
-
打开终端,编辑你的
~/.zshrc
文件(macOS 默认使用 Zsh,或者你使用的是 Bash 则编辑~/.bash_profile
):nano ~/.zshrc
-
在文件末尾添加以下内容:
# Go 环境变量配置 export GOPATH=$HOME/go export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
GOPATH
:这是 Go 的工作区目录,存储你的 Go 项目和依赖。默认建议使用~/go
目录。PATH
:将 Go 的二进制文件添加到PATH
中,确保你可以在任何地方使用 Go 的命令。
-
保存文件并退出(按
Ctrl + O
保存,按Ctrl + X
退出)。 -
运行以下命令使配置立即生效:
source ~/.zshrc
检查 Go 是否正确安装
在终端输入以下命令来检查 Go 是否成功安装:
go version
如果安装成功,你会看到类似以下的输出:
go version go1.23.1 darwin/arm64
验证安装:编写第一个 Go 程序
接下来,我们编写一个简单的 Go 程序来验证安装是否成功。
创建 Go 工作区
首先,在 GOPATH
中创建一个 Go 项目的工作目录:
mkdir -p ~/go/src/hello
cd ~/go/src/hello
编写 Hello World 程序
在该目录下创建一个名为 main.go
的文件,并写入以下内容:
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
运行程序
在终端中运行以下命令来编译并执行这个 Go 程序:
go run main.go
你应该会看到以下输出:
Hello, Go!
编译程序
如果你希望将程序编译成可执行文件,可以使用 go build
命令:
go build
编译完成后,你会在当前目录下看到一个名为 hello
(或者 hello.exe
,如果你在 Windows 上运行)的可执行文件。你可以直接运行它:
./hello
你将再次看到 Hello, Go!
的输出。
常用的 Go 命令
以下是一些常用的 Go 命令,可以帮助你在开发中快速上手:
go run
:直接运行 Go 源代码文件。go build
:编译源代码,生成可执行文件。go install
:编译并安装包和依赖。go fmt
:格式化 Go 源代码文件。go test
:运行单元测试。go get
:下载和安装远程包。go doc
:查看 Go 包的文档。
Go语言基础语法
1. 变量(Variables)
在 Go 中,变量通过 var
关键字声明。
package main
import "fmt"
func main() {
// 声明一个整型变量
var a int = 10
// 声明一个字符串变量
var str string = "Hello, Go!"
// 使用类型推断(编译器会自动推断类型)
var b = 20
// 简短声明(只能在函数内使用)
c := 30
fmt.Println(a, str, b, c)
}
变量相关知识:
- 使用
var
关键字可以声明变量。 - 通过
:=
进行简短声明,不需要var
关键字,只能在函数内部使用。 - Go 是静态类型语言,变量类型在编译时就确定,不能改变。
2. 常量(Constants)
常量通过 const
关键字声明,常量的值在编译时就确定,不能被修改。
package main
import "fmt"
func main() {
const pi = 3.14159
const str = "Hello, Go!"
fmt.Println(pi, str)
}
常量相关知识:
- 常量的值必须是编译时确定的,不能运行时赋值。
- 常量可以是数值、字符串、布尔值等。
3. 计算符号(Operators)
Go 支持常见的算术运算符、关系运算符和逻辑运算符:
package main
import "fmt"
func main() {
a, b := 10, 20
// 算术运算符
fmt.Println("加法: ", a+b)
fmt.Println("减法: ", a-b)
fmt.Println("乘法: ", a*b)
fmt.Println("除法: ", a/b)
fmt.Println("取模: ", a%b)
// 关系运算符
fmt.Println("相等: ", a == b)
fmt.Println("不等: ", a != b)
fmt.Println("大于: ", a > b)
fmt.Println("小于: ", a < b)
// 逻辑运算符
fmt.Println("逻辑与: ", a > 5 && b > 15)
fmt.Println("逻辑或: ", a > 15 || b > 15)
fmt.Println("逻辑非: ", !(a > 15))
}
4. 字符串(Strings)
字符串是不可变的 Unicode 字符序列,可以通过 "
包含字符串,或者通过反引号
包含多行字符串。
package main
import "fmt"
func main() {
var str1 string = "Hello"
str2 := "World"
// 多行字符串
str3 := `这是
多行
字符串`
// 字符串拼接
combined := str1 + ", " + str2
fmt.Println(combined)
fmt.Println(str3)
}
字符串相关知识:
- 使用
+
可以拼接字符串。 - 使用反引号可以定义多行字符串。
5. 条件表达式(Condition Expressions)
Go 中的条件表达式使用 if
和 else
,条件表达式不需要用括号包裹。
package main
import "fmt"
func main() {
a := 10
if a > 5 {
fmt.Println("a 大于 5")
} else {
fmt.Println("a 小于或等于 5")
}
// 带初始化的 if 语句
if b := a * 2; b > 15 {
fmt.Println("b 大于 15")
} else {
fmt.Println("b 小于或等于 15")
}
}
6. 分支表达式(Switch Statements)
switch
语句是 Go 中多分支选择的常用方法。
package main
import "fmt"
func main() {
a := 2
switch a {
case 1:
fmt.Println("a 等于 1")
case 2:
fmt.Println("a 等于 2")
default:
fmt.Println("a 不等于 1 或 2")
}
}
switch
的特点:
switch
不需要像其他语言中那样使用break
,每个case
执行完毕后会自动结束。- 可以在
case
中进行条件匹配。
7. 循环(Loops)
Go 只有一种循环结构 for
,没有 while
和 do-while
。
package main
import "fmt"
func main() {
// 基本的 for 循环
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// 类似 while 的 for 循环
i := 0
for i < 5 {
fmt.Println(i)
i++
}
// 无限循环
// for {
// fmt.Println("无限循环")
// }
}
8. 数组(Arrays)
数组是具有固定长度的同类型元素序列。
package main
import "fmt"
func main() {
var arr [5]int // 定义一个长度为5的数组
arr[0] = 1
arr[1] = 2
// 初始化时直接赋值
arr2 := [3]int{1, 2, 3}
fmt.Println(arr)
fmt.Println(arr2)
}
数组相关知识:
- 数组的长度是固定的,一旦声明,长度就不能改变。
9. 切片(Slices)
切片是基于数组的动态大小的序列,比数组更加灵活。
package main
import "fmt"
func main() {
// 声明并初始化切片
var slice []int = []int{1, 2, 3}
// 使用 append 添加元素
slice = append(slice, 4)
// 通过切片创建切片
newSlice := slice[1:3]
fmt.Println(slice)
fmt.Println(newSlice)
}
切片相关知识:
- 切片是一种引用类型,指向底层数组。
- 使用
append
函数可以动态地向切片添加元素。
10. Map(字典)
Map 是键值对的集合,可以通过键快速查找对应的值。
package main
import "fmt"
func main() {
// 创建一个 map
var myMap map[string]int = map[string]int{
"Alice": 25,
"Bob": 30,
}
// 添加元素
myMap["Charlie"] = 35
// 查找元素
age := myMap["Alice"]
// 判断键是否存在
age, exists := myMap["David"]
if exists {
fmt.Println("David 的年龄是", age)
} else {
fmt.Println("David 不存在")
}
fmt.Println(myMap)
}
11. 简单函数(Functions)
Go 中的函数通过 func
关键字定义,可以返回多个值。
package main
import "fmt"
// 定义一个返回两个整数相加的函数
func add(a int, b int) int {
return a + b
}
// 定义一个返回多个值的函数
func swap(a, b int) (int, int) {
return b, a
}
func main() {
result := add(3, 4)
fmt.Println("3 + 4 =", result)
x, y := swap(10, 20)
fmt.Println("交换后:", x, y)
}
函数相关知识:
- 函数可以有多个返回值。
- 参数类型相同的参数可以合并写在一起,如
(a, b int)
。 - 返回值类型写在函数名和参数列表之后。
两个小练习
练习 1:计算多个人的平均体脂
- 实现完整的体脂计算器
- 连续输入三人的姓名、性别、身高、体重、年龄信息
- 计算每个人的 BMI、体脂率
输出:
- 每个人姓名、BMI、体脂率、建议
- 总人数、平均体脂率
运用到的知识:
- 变量和常量:
- 声明和使用变量来存储用户输入的身高、体重、年龄等信息。
- 使用
:=
进行简短声明,比如a := 10
。
- 输入和输出:
- 使用
fmt.Scanln
来获取用户输入,并使用fmt.Println
输出结果。
- 使用
- 条件表达式:
- 使用
if
语句来判断体脂率的不同范围,并给出不同的健康建议。
- 使用
- 循环:
- 使用
for
循环来处理多个人的输入和体脂计算。
- 使用
- 函数:
- 编写了多个函数,如
calculateBMI
、calculateBodyFatRate
和giveSuggestion
,这些函数帮助简化代码结构并实现代码复用。
- 编写了多个函数,如
- 数组和切片:
- 使用了数组来存储每个人的信息,如身高、体重、年龄等。
- 切片可以方便地处理可变大小的数据集合。
package main
import (
"fmt"
)
func main() {
// 定义数组来存储姓名、性别、身高、体重、年龄、BMI 和体脂率
var names [3]string
var genders [3]string
var heights [3]float64
var weights [3]float64
var ages [3]int
var BMIs [3]float64
var bodyFatRates [3]float64
// 输入三个人的信息
for i := 0; i < 3; i++ {
fmt.Printf("请输入第 %d 个人的信息:\n", i+1)
fmt.Print("姓名: ")
fmt.Scan(&names[i])
fmt.Print("性别 (male/female): ")
fmt.Scan(&genders[i])
fmt.Print("身高 (米): ")
fmt.Scan(&heights[i])
fmt.Print("体重 (公斤): ")
fmt.Scan(&weights[i])
fmt.Print("年龄: ")
fmt.Scan(&ages[i])
// 计算 BMI
BMIs[i] = calculateBMI(heights[i], weights[i])
// 计算体脂率
bodyFatRates[i] = calculateBodyFatRate(genders[i], BMIs[i], ages[i])
}
// 输出每个人的BMI、体脂率和建议
for i := 0; i < 3; i++ {
fmt.Printf("\n姓名: %s\n", names[i])
fmt.Printf("BMI: %.2f\n", BMIs[i])
fmt.Printf("体脂率: %.2f%%\n", bodyFatRates[i])
fmt.Println(giveSuggestion(bodyFatRates[i], genders[i]))
}
// 计算并输出平均体脂率
calculateAndPrintAverageBodyFatRate(bodyFatRates[:])
}
// 计算 BMI
func calculateBMI(height, weight float64) float64 {
return weight / (height * height)
}
// 计算体脂率
func calculateBodyFatRate(gender string, BMI float64, age int) float64 {
if gender == "male" {
return (1.20 * BMI) + (0.23 * float64(age)) - 16.2
}
return (1.20 * BMI) + (0.23 * float64(age)) - 5.4
}
// 根据体脂率给出建议
func giveSuggestion(bodyFatRate float64, gender string) string {
var suggestion string
if gender == "male" {
switch {
case bodyFatRate < 10:
suggestion = "建议:过瘦,适当增加体重。"
case bodyFatRate >= 10 && bodyFatRate < 20:
suggestion = "建议:体脂正常,保持良好状态。"
case bodyFatRate >= 20 && bodyFatRate < 25:
suggestion = "建议:体脂偏高,适当控制体重。"
default:
suggestion = "建议:肥胖,建议制定减脂计划。"
}
} else {
switch {
case bodyFatRate < 20:
suggestion = "建议:过瘦,适当增加体重。"
case bodyFatRate >= 20 && bodyFatRate < 30:
suggestion = "建议:体脂正常,保持良好状态。"
case bodyFatRate >= 30 && bodyFatRate < 35:
suggestion = "建议:体脂偏高,适当控制体重。"
default:
suggestion = "建议:肥胖,建议制定减脂计划。"
}
}
return suggestion
}
// 计算并输出平均体脂率
func calculateAndPrintAverageBodyFatRate(bodyFatRates []float64) {
totalBodyFat := 0.0
for _, bodyFatRate := range bodyFatRates {
totalBodyFat += bodyFatRate
}
averageBodyFatRate := totalBodyFat / float64(len(bodyFatRates))
fmt.Printf("\n总人数: %d\n", len(bodyFatRates))
fmt.Printf("平均体脂率: %.2f%%\n", averageBodyFatRate)
}
说明:
-
数组存储:
- 使用
names
、genders
、heights
、weights
、ages
、BMIs
和bodyFatRates
这几个数组分别存储每个人的信息。 - 每个数组的长度为3,适用于存储三个人的数据。
- 使用
-
函数封装:
calculateBMI
:根据身高和体重计算 BMI。calculateBodyFatRate
:根据性别、BMI 和年龄计算体脂率。giveSuggestion
:根据体脂率给出建议。calculateAndPrintAverageBodyFatRate
:计算并输出平均体脂率。
-
示例输入/输出:
请输入第 1 个人的信息:
姓名: 张三
性别 (male/female): male
身高 (米): 1.75
体重 (公斤): 70
年龄: 30请输入第 2 个人的信息:
姓名: 李四
性别 (male/female): female
身高 (米): 1.65
体重 (公斤): 68
年龄: 28请输入第 3 个人的信息:
姓名: 王五
性别 (male/female): male
身高 (米): 1.80
体重 (公斤): 85
年龄: 35姓名: 张三
BMI: 22.86
体脂率: 19.55%
建议:体脂正常,保持良好状态。姓名: 李四
BMI: 24.98
体脂率: 31.27%
建议:肥胖,建议制定减脂计划。姓名: 王五
BMI: 26.23
体脂率: 24.80%
建议:体脂偏高,适当控制体重。总人数: 3
平均体脂率: 25.87%
练习 2:判断两条线是否平行
编写一个程序来判断两条直线是否平行。你需要通过两点计算每条线的斜率,并比较两条线的斜率是否相等,来确定它们是否平行。
运用到的知识:
-
变量:
- 使用
var
和:=
声明变量,存储用户输入的点坐标。
- 使用
-
输入和输出:
- 使用
fmt.Scanln
获取用户输入,并用fmt.Println
输出结果。
- 使用
-
条件表达式:
- 使用
if
语句来判断两条线是否垂直(即 x 坐标相同),如果两条线都垂直,它们就是平行的。
- 使用
-
函数:
- 使用了函数
calculateSlope
来计算斜率,areLinesParallel
来判断两条线是否平行,这些函数让代码结构更加清晰。
- 使用了函数
通过比较两条线的斜率来判断它们是否平行。两点决定一条直线,斜率的公式为: 斜率 = y 2 − y 1 x 2 − x 1 \text{斜率} = \frac{y_2 - y_1}{x_2 - x_1} 斜率=x2−x1y2−y1。如果两条线的斜率相同,那么它们就是平行的。需要注意的是,当两点的 x 1 = = x 2 x_1 == x_2 x1==x2 时,斜率不存在(垂直线)。
package main
import (
"fmt"
)
// 计算两点间的斜率
func calculateSlope(x1, y1, x2, y2 float64) (float64, bool) {
// 如果两点的x坐标相同,斜率不存在,返回false表示斜率不存在(垂直线)
if x1 == x2 {
return 0, false
}
// 计算斜率
slope := (y2 - y1) / (x2 - x1)
return slope, true
}
// 判断两条直线是否平行
func areLinesParallel(x1, y1, x2, y2, x3, y3, x4, y4 float64) bool {
// 计算两条线的斜率
slope1, exists1 := calculateSlope(x1, y1, x2, y2)
slope2, exists2 := calculateSlope(x3, y3, x4, y4)
// 如果一条线是垂直线,而另一条不是,则不平行
if !exists1 && !exists2 {
return true // 两条线都垂直,平行
} else if !exists1 || !exists2 {
return false // 一条垂直,另一条不垂直,不平行
}
// 如果斜率相等,说明平行
return slope1 == slope2
}
func main() {
var x1, y1, x2, y2 float64
var x3, y3, x4, y4 float64
// 输入第一条线的两点坐标
fmt.Println("请输入第一条直线的两个点坐标:")
fmt.Print("点1 (x1, y1): ")
fmt.Scan(&x1, &y1)
fmt.Print("点2 (x2, y2): ")
fmt.Scan(&x2, &y2)
// 输入第二条线的两点坐标
fmt.Println("请输入第二条直线的两个点坐标:")
fmt.Print("点3 (x3, y3): ")
fmt.Scan(&x3, &y3)
fmt.Print("点4 (x4, y4): ")
fmt.Scan(&x4, &y4)
// 判断两条线是否平行
if areLinesParallel(x1, y1, x2, y2, x3, y3, x4, y4) {
fmt.Println("这两条线是平行的")
} else {
fmt.Println("这两条线不平行")
}
}
说明:
calculateSlope
:计算给定两点间的斜率,如果两点的x
坐标相同,返回false
表示垂直线。areLinesParallel
:比较两条线的斜率,判断它们是否平行。如果斜率相等,则平行;如果一条线垂直而另一条线不垂直,则不平行。
示例输入/输出:
请输入第一条直线的两个点坐标:
点1 (x1, y1): 1 1
点2 (x2, y2): 3 3
请输入第二条直线的两个点坐标:
点3 (x3, y3): 2 2
点4 (x4, y4): 4 4
这两条线是平行的