超实用的Go语言基础教程,让你快速上手刷题!!

背景😎

工欲善其事,必先利其器。掌握Go的基础语法还不够,还需要勤加练习,修习“外功”,才能达到出奇制胜的效果。

在大致了解Go语言的基本语法后,我就迫不得已地想使用这门语言。可是我发现编程思路不是问题,很大的问题是“手慢”,不熟悉常用写法(可能这就是快速过语法的缺点吧,脑子会了,手没会)φ(* ̄0 ̄)。

在我看来,用Go语言刷算法题是一个非常好的练习“外功”的法门,可以帮助我提高思维的灵敏性和解决抽象化问题的能力。更重要地是复习我学习过的语法知识,不然真的很容易忘。虽然它和C语言有点像,但是我也并不经常使用C,两者不太好建立起清晰的关联图。因此,我会一边勤能补拙,一边总结一些语法知识,一边建立语言之间的联系,方便我加深记忆。

我刷的不是Leetcode形式的题目,而是ACM形式的题目。因为ACM形式需要处理输入输出,这对我的要求会更高点。

刷题平台:洛谷

基础知识🤔

输入处理

Go接收输入的方式有四类,分别是 fmt 包中的 Scan 、Scanf 和Scanln函数以及bufio.Scanner对象实现。

  • Scan函数

使用场景:可以用于读取一段空格分隔的字符串或多个数值类型的输入,例如读取数字或时间等;

示例一:计算浮点数相除的余。

输入格式:输入仅一行,包括两个双精度浮点数a和b。

输入样例:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-undefined">13.55 24.88
</code></span></span>

处理方式:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#008000">// 接收两个双精度浮点数a,b</span>
	<span style="color:#0000ff">var</span> a, b <span style="color:#a31515">float64</span>
	_, err := fmt.Scan(&a, &b)
	<span style="color:#0000ff">if</span> err != <span style="color:#a31515">nil</span> {
		fmt.Println(err)
	}
}
</code></span></span>
  • Scanf函数

使用场景:适用于需要按特定格式读取和处理输入数据的场景,例如读取时间、日期、金额等;

示例二:数字排序

输入格式:输入三个数字,数字之间用逗号隔开。

输入样例:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-undefined">1,4,6
</code></span></span>

处理方式:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#0000ff">var</span> a, b, c <span style="color:#a31515">int</span>
	fmt.Scanf(<span style="color:#a31515">"%d,%d,%d"</span>, &a, &b, &c)
	fmt.Println(a, b, c)
}
</code></span></span>

如果输入不止三个数字,输入很长怎么办?

我想到的是直接当字符串保存,然后用“,”分割每一个元素,获得一个字符串数组,最后利用Atoi函数将字符串转为整数,存储到一个新的int类型数组中。

具体做法如下:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"bufio"</span>
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"os"</span>
	<span style="color:#a31515">"strconv"</span>
	<span style="color:#a31515">"strings"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#0000ff">var</span> input <span style="color:#a31515">string</span>
	scanner := bufio.NewScanner(os.Stdin)
	<span style="color:#0000ff">if</span> scanner.Scan() {
		input = scanner.Text()
	} <span style="color:#0000ff">else</span> {
		fmt.Println(<span style="color:#a31515">"Error"</span>)
	}
	strArray := strings.Split(input, <span style="color:#a31515">","</span>)
	intArray := <span style="color:#0000ff">make</span>([]<span style="color:#a31515">int</span>, <span style="color:#0000ff">len</span>(strArray))  <span style="color:#008000">// 根据strArray的长度确定intArraye的长度</span>
	<span style="color:#0000ff">for</span> i, v := <span style="color:#0000ff">range</span> strArray {
		<span style="color:#0000ff">var</span> err <span style="color:#a31515">error</span>
		intArray[i], err = strconv.Atoi(strings.TrimSpace(v))  <span style="color:#008000">// strings.TrimSpace 函数去掉字符串中的多余空白字符</span>
		<span style="color:#0000ff">if</span> err != <span style="color:#a31515">nil</span> {
			fmt.Println(<span style="color:#a31515">"Error"</span>)
		}
	}
	fmt.Printf(<span style="color:#a31515">"The input integers are: %v\n"</span>, intArray)
}
</code></span></span>
  • Scanln函数

使用场景:适用于读取空格或换行分隔的字符串或多个数值类型的输入,例如读取单词或名称等。用法和Scan相似,就不举例子了。(~ ̄▽ ̄)~

  • bufio.Scanner对象

使用场景:这个对象可以从标准输入中逐行读取输入,直到遇到文件结尾或输入流关闭为止。特别适合循环读入数据!

示例三:字符串读取,并打印

输入格式:输入多行英文句子。

输入样例:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-sql">wow<span style="color:#ab5656">!</span>
you <span style="color:#0000ff">are</span> pretty good <span style="color:#0000ff">at</span> printing<span style="color:#ab5656">!</span>
you win.
</code></span></span>

处理方式:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"bufio"</span>
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"os"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#0000ff">var</span> strArray []<span style="color:#a31515">string</span>
	scanner := bufio.NewScanner(os.Stdin)
	<span style="color:#0000ff">for</span> scanner.Scan() {
		input := scanner.Text()
		<span style="color:#0000ff">if</span> input == <span style="color:#a31515">""</span> {
			<span style="color:#0000ff">break</span>
		}
		strArray = <span style="color:#0000ff">append</span>(strArray, input)
	}
	<span style="color:#0000ff">if</span> err := scanner.Err(); err != <span style="color:#a31515">nil</span> {
		fmt.Printf(<span style="color:#a31515">"Error reading standard input: %s\n"</span>, err.Error())
	}

	fmt.Printf(<span style="color:#a31515">"Read %d lines:\n"</span>, <span style="color:#0000ff">len</span>(strArray))
	<span style="color:#0000ff">for</span> i, line := <span style="color:#0000ff">range</span> strArray {
		fmt.Printf(<span style="color:#a31515">"%d: %s\n"</span>, i+<span style="color:#880000">1</span>, line)
	}
}

</code></span></span>

输出处理

Go处理输出的方式根据场景的不同,可以分为以下几种:

  • 终端或控制台中输出一些信息,使用fmt包中的函数。
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	name := <span style="color:#a31515">"Tom"</span>
	age := <span style="color:#880000">18</span>
	fmt.Println(<span style="color:#a31515">"name:"</span>, name, <span style="color:#a31515">"age:"</span>, age) <span style="color:#008000">// Println()函数会自动添加空格</span>
	fmt.Printf(<span style="color:#a31515">"name: %s age: %d\n"</span>, name, age)
	str1 := fmt.Sprintf(<span style="color:#a31515">"name: %s age: %d\n"</span>, name, age) <span style="color:#008000">// Sprintf()函数会返回一个字符串</span>
	fmt.Printf(str1)
}
</code></span></span>
  • 记录程序运行过程中的日志信息时,可以使用log包中的函数。
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"log"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	log.Println(<span style="color:#a31515">"Starting the application..."</span>)
	fmt.Println(<span style="color:#a31515">"Hello, World!"</span>)
	log.Println(<span style="color:#a31515">"Terminating the application..."</span>)
}
</code></span></span>
  • 读写文件或网络连接时,可以使用os包中的函数。
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"log"</span>
	<span style="color:#a31515">"os"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	file, err := os.Open(<span style="color:#a31515">"test.txt"</span>)
	<span style="color:#0000ff">if</span> err != <span style="color:#a31515">nil</span> {
		log.Fatal(err)
	}
	<span style="color:#0000ff">defer</span> file.Close()

	buffer := <span style="color:#0000ff">make</span>([]<span style="color:#a31515">byte</span>, <span style="color:#880000">1024</span>) <span style="color:#008000">// read 1024 bytes at a time</span>
	<span style="color:#0000ff">for</span> {
		bytesRead, err := file.Read(buffer) <span style="color:#008000">// read bytes from file</span>
		<span style="color:#0000ff">if</span> err != <span style="color:#a31515">nil</span> {
			log.Fatal(err)
		}
		fmt.Println(<span style="color:#a31515">"bytes read: "</span>, bytesRead)
		fmt.Println(<span style="color:#a31515">"bytes:"</span>, buffer[:bytesRead])
		<span style="color:#0000ff">if</span> bytesRead < <span style="color:#880000">1024</span> {
			<span style="color:#0000ff">break</span>
		}
	}
	fmt.Printf(<span style="color:#a31515">"File contents: %s"</span>, buffer) <span style="color:#008000">// print file contents</span>
}
</code></span></span>
  • 执行系统命令或创建进程时,可以使用os包中的函数。
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"log"</span>
	<span style="color:#a31515">"os"</span>
	<span style="color:#a31515">"os/exec"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	cmd := exec.Command(<span style="color:#a31515">"whoami"</span>)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	<span style="color:#0000ff">if</span> err != <span style="color:#a31515">nil</span> {
		log.Fatal(err)
	}
	fmt.Println(<span style="color:#a31515">"Done"</span>)
}
</code></span></span>

ACM形式的题目更多考察的是第一种在终端/控制台输出信息的格式。这个就要涉及到Go语言格式化字符串的方式的知识点。在我看来,格式化字符串在每种语言里都享有很高的地位。毕竟更美观的打印数据,也有助于我们更好的理解信息。

  • 格式化字符串
格式描述
%v表示按照值的默认格式输出,可以输出任意类型的数据。
%s表示输出字符串类型的数据。
%d表示输出十进制整数类型的数据。
%f表示输出浮点数类型的数据。
%t表示输出布尔类型的数据,true和false分别对应输出1和0。
%p表示输出指针类型的数据。
%c表示输出字符类型的数据。
%q表示输出带引号的字符串类型的数据。
%b表示输出二进制数类型的数据。
%x表示输出十六进制数类型的数据。
%o表示输出八进制数类型的数据。
%05d表示输出5位,不足的位数用0补齐。
%.2f表示输出小数点后两位。
%10s输出10个字符长度,不足的位数用空格补齐
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> <span style="color:#a31515">"fmt"</span>

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
    name := <span style="color:#a31515">"Tom"</span>
    age := <span style="color:#880000">18</span>
    height := <span style="color:#880000">1.75</span>

    fmt.Printf(<span style="color:#a31515">"My name is %s, I'm %d years old, and I'm %.2f meters tall.\n"</span>, name, age, height)
    fmt.Printf(<span style="color:#a31515">"My name is %10s, I'm %05d years old, and I'm %.2f meters tall.\n"</span>, name, age, height)
}
</code></span></span>
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-delphi">My <span style="color:#0000ff">name</span> <span style="color:#0000ff">is</span> Tom, I<span style="color:#a31515">'m 18 years old, and I'</span>m <span style="color:#880000">1.75</span> meters tall.
My <span style="color:#0000ff">name</span> <span style="color:#0000ff">is</span>        Tom, I<span style="color:#a31515">'m 00018 years old, and I'</span>m <span style="color:#880000">1.75</span> meters tall.
</code></span></span>

数组?切片?

在Go语言中,数组是一种固定长度的数据结构,一旦定义了数组的长度,就无法再向数组中添加新的元素。如果想动态更改,可以考虑使用切片。根据使用方法可以大致分个类:

共性差异
下标访问定义方式不同
循环遍历切片可以添加/删除元素
长度计算
切片[start:end]
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#0000ff">var</span> n <span style="color:#a31515">int</span>
	fmt.Scan(&n)
	<span style="color:#0000ff">var</span> arr []<span style="color:#a31515">int</span>
	<span style="color:#0000ff">var</span> max <span style="color:#a31515">int</span> = <span style="color:#880000">-10000000</span>
	<span style="color:#0000ff">var</span> sum <span style="color:#a31515">int</span> = <span style="color:#880000">0</span>
	<span style="color:#0000ff">for</span> i := <span style="color:#880000">0</span>; i < n; i++ {
		<span style="color:#0000ff">var</span> x <span style="color:#a31515">int</span>
		fmt.Scan(&x)
		sum += x
		<span style="color:#0000ff">if</span> max < x {
			max = x
		}
		arr = <span style="color:#0000ff">append</span>(arr, x)
	}
	<span style="color:#0000ff">var</span> count <span style="color:#a31515">int</span> = <span style="color:#880000">0</span>
	<span style="color:#008000">// 找到数组里面最大的数及它出现的次数</span>
	<span style="color:#0000ff">for</span> i := <span style="color:#880000">0</span>; i < n; i++ {
		<span style="color:#0000ff">if</span> max == arr[i] {
			count++
		}
	}
	fmt.Println(sum - max*count)
}
</code></span></span>
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#0000ff">var</span> n <span style="color:#a31515">int</span>
	fmt.Scan(&n)
	<span style="color:#0000ff">var</span> used [<span style="color:#880000">110</span>]<span style="color:#a31515">int</span>
	<span style="color:#0000ff">for</span> i := <span style="color:#880000">0</span>; i < n; i++ {
		<span style="color:#0000ff">var</span> x <span style="color:#a31515">int</span>
		fmt.Scan(&x)
		used[x]++
		<span style="color:#0000ff">if</span> used[x] < <span style="color:#880000">2</span> {
			fmt.Print(x, <span style="color:#a31515">" "</span>)
		}
	}
}
</code></span></span>

字符串处理

  • 字符串长度计算

在Go语言中,字符串的长度是指字符串中字节的个数,而不是字符的个数。对于包含非ASCII字符的字符串,一个字符可能会占用多个字节。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	str := <span style="color:#a31515">"hello world"</span>
	fmt.Println(<span style="color:#0000ff">len</span>(str)) <span style="color:#008000">// 输出11</span>
	str = <span style="color:#a31515">"hello 世界"</span>
	fmt.Println(<span style="color:#0000ff">len</span>(str)) <span style="color:#008000">// 输出12</span>
}
</code></span></span>
  • 字符串遍历

既可以使用传统的下标遍历,也可以使用range遍历。建议使用range遍历,因为当字符串中出现中文时,下标遍历获取的是byte类型的值,也就意味着它是将一个汉字拆成了3个byte类型字节分别输出。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	str := <span style="color:#a31515">"hello world"</span>
	<span style="color:#0000ff">for</span> i, v := <span style="color:#0000ff">range</span> str {
		fmt.Printf(<span style="color:#a31515">"字符串中下标为 %d 的字符是 %c\n"</span>, i, v)
	}
}
</code></span></span>
  • 字符串切片

需要注意的是,在使用字符串切片时,下标是按字节计算的,而不是按字符计算的。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">str := <span style="color:#a31515">"hello world"</span>
slice := str[<span style="color:#880000">1</span>:<span style="color:#880000">5</span>]  <span style="color:#008000">// 获取str中下标为1到4的字符,不包括下标为5的字符</span>
fmt.Println(slice)  <span style="color:#008000">// 输出"ello"</span>
</code></span></span>
  • 字符串连接

可以使用加号运算符或fmt.Sprintf函数来连接字符串。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">str1 := <span style="color:#a31515">"hello"</span>
str2 := <span style="color:#a31515">"world"</span>
str3 := str1 + <span style="color:#a31515">" "</span> + str2  <span style="color:#008000">// 使用加号运算符连接字符串</span>
fmt.Println(str3)  <span style="color:#008000">// 输出"hello world"</span>

str4 := fmt.Sprintf(<span style="color:#a31515">"%s %s"</span>, str1, str2)  <span style="color:#008000">// 使用fmt.Sprintf函数连接字符串</span>
fmt.Println(str4)  <span style="color:#008000">// 输出"hello world"</span>
</code></span></span>
  • 字符串查找

使用strings包中的函数来查找字符串中的子串。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">str := <span style="color:#a31515">"hello world"</span>
index := strings.Index(str, <span style="color:#a31515">"world"</span>)  <span style="color:#008000">// 查找子串"world"在str中的位置</span>
fmt.Println(index)  <span style="color:#008000">// 输出6</span>
</code></span></span>
  • 字符串替换

使用strings包中的函数来替换字符串中的子串。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">str := <span style="color:#a31515">"hello world"</span>
newstr := strings.Replace(str, <span style="color:#a31515">"world"</span>, <span style="color:#a31515">"golang"</span>, <span style="color:#880000">-1</span>) <span style="color:#008000">// 将子串"world"替换为"golang", -1表示全部替换</span>
fmt.Println(newstr)                                   <span style="color:#008000">// 输出"hello golang"</span>
</code></span></span>
  • 字符串转换

使用strconv包中的函数进行转换。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">str := <span style="color:#a31515">"123"</span>
num, err := strconv.Atoi(str) <span style="color:#008000">// 将字符串转换为整型</span>
<span style="color:#0000ff">if</span> err != <span style="color:#a31515">nil</span> {
    fmt.Println(<span style="color:#a31515">"转换失败"</span>)
} <span style="color:#0000ff">else</span> {
    fmt.Printf(<span style="color:#a31515">"转换结果是 %T\n"</span>, num)
}

num = <span style="color:#880000">123</span>
str = strconv.Itoa(num) <span style="color:#008000">// 将整型转换为字符串</span>
fmt.Printf(<span style="color:#a31515">"转换结果是 %T\n"</span>, str)
</code></span></span>
  • 正则匹配(✨✨✨✨)
预定义字符集描述
\d匹配一个数字字符。等价于字符集 [0-9]。
\s匹配一个空白字符(空格、制表符、换行符等)。等价于字符集 [ \t\n\r\f\v]。
\w匹配一个单词字符。等价于字符集 [a-zA-Z0-9_]。
\W匹配一个非单词字符。等价于字符集 [^a-zA-Z0-9_]。
\S匹配一个非空白字符。等价于字符集 [^ \t\n\r\f\v]。
\D匹配一个非数字字符。等价于字符集 [^0-9]。
\b表示单词边界,我的理解是能准确匹配到某个单词,不把包含这个单词的前缀词算在内。比如gotest就无法匹配test。

匹配一个由汉字组成的字符串(数据清洗时常用!):

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-ruby">^[\u4e00-\u9fa5]+<span style="color:#008000">$
</span></code></span></span>

匹配一个由邮箱地址组成的字符串(匹配恶意URL、匹配钓鱼邮箱常用):

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-css">^\w+(<span style="color:#2b91af">[-+.]</span>\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
</code></span></span>

演示1:匹配一个字符串是否符合某个正则表达式。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"regexp"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#008000">// 定义一个正则表达式</span>
	pattern := <span style="color:#a31515">"^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"</span>
	<span style="color:#008000">// 编译正则表达式</span>
	reg := regexp.MustCompile(pattern)
	<span style="color:#008000">// 要匹配的字符串</span>
	str := <span style="color:#a31515">"abc123@11-2.com"</span>
	<span style="color:#008000">// 判断字符串是否匹配</span>
	matched := reg.MatchString(str)
	fmt.Println(matched)
}
</code></span></span>

演示2:利用正则进行查找和替换字符串

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#008000">// 查找</span>
str := <span style="color:#a31515">"hello world"</span>
re := regexp.MustCompile(<span style="color:#a31515">`\b\w+o\w+\b`</span>) <span style="color:#008000">// 匹配包含字母o的单词</span>
newstr := re.FindAllString(str, <span style="color:#880000">-1</span>)     <span style="color:#008000">// 将查找所有匹配的字符串</span>
fmt.Println(newstr)

<span style="color:#008000">// 替换</span>
str := <span style="color:#a31515">"hello world"</span>
re := regexp.MustCompile(<span style="color:#a31515">`\b\w+o\w+\b`</span>)  <span style="color:#008000">// 匹配包含字母o的单词</span>
newstr := re.ReplaceAllString(str, <span style="color:#a31515">"golang"</span>)  <span style="color:#008000">// 将所有匹配的字符串替换为"golang"</span>
fmt.Println(newstr)  <span style="color:#008000">// 输出"golang golang"</span>
</code></span></span>
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"bufio"</span>
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"os"</span>
)

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	sentence := scanner.Text()
	<span style="color:#0000ff">var</span> count <span style="color:#a31515">int</span> = <span style="color:#880000">0</span>
	<span style="color:#0000ff">for</span> _, v := <span style="color:#0000ff">range</span> sentence {
		<span style="color:#0000ff">if</span> v >= <span style="color:#a31515">'0'</span> && v <= <span style="color:#a31515">'9'</span> {
			count++
		}
	}
	fmt.Println(count)
}
</code></span></span>

结构体

Go语言的结构体和C语言很相似。

  • 结构体定义
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">type</span> Person <span style="color:#0000ff">struct</span> {
    Name <span style="color:#a31515">string</span>
    Age <span style="color:#a31515">int</span>
    Height <span style="color:#a31515">float32</span>
}
</code></span></span>
  • 结构体初始化
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">p1 := Person{Name: <span style="color:#a31515">"Alice"</span>, Age: <span style="color:#880000">20</span>, Height: <span style="color:#880000">1.65</span>}  <span style="color:#008000">// 定义一个Person类型的结构体变量p1并初始化</span>
</code></span></span>
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">p2 := <span style="color:#0000ff">new</span>(Person)  <span style="color:#008000">// 定义一个指向Person类型的指针变量p2,并分配内存空间</span>
</code></span></span>
  • 结构体元素访问("."号访问)

指针和普通的对象类型都是使用“.”号访问。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go">p1.Name = <span style="color:#a31515">"Alice"</span>  <span style="color:#008000">// 给p1的Name赋值为"Alice"</span>
p1.Age = <span style="color:#880000">20</span>  <span style="color:#008000">// 给p1的Age赋值为20</span>
p1.Height = <span style="color:#880000">1.65</span>  <span style="color:#008000">// 给p1的Height赋值为1.65</span>
</code></span></span>

分界线:———————————————————————————————————————

Go还支持一些面向对象的编程特性,非常的灵活和强大!!!

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">func</span> (p *Person) GetInfo() <span style="color:#a31515">string</span> {
    <span style="color:#0000ff">return</span> fmt.Sprintf(<span style="color:#a31515">"Name: %s, Age: %d, Height: %.2f"</span>, p.Name, p.Age, p.Height)
}

p1.GetInfo()  <span style="color:#008000">// 调用p1的GetInfo方法,返回"Name: Alice, Age: 20, Height: 1.65"</span>
</code></span></span>

这个方法定义了一个指针类型为Person的方法GetInfo,用来返回一个包含Person对象信息的字符串。我们可以通过调用结构体变量的方法来实现对结构体对象的操作。这种使用方法就很棒!这就有点像类方法,GetInfo函数就是Person结构体的类方法。想要使用这个方法,那么就需要先构造一个Person的结构体对象,然后通过对象调用。

此外,Go还支持封装、继承、多态的特性,用来实现复杂的对象模型和数据结构。

  • 封装
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">type</span> Person <span style="color:#0000ff">struct</span> {
    name <span style="color:#a31515">string</span>
    age <span style="color:#a31515">int</span>
}

<span style="color:#0000ff">func</span> (p *Person) SetName(name <span style="color:#a31515">string</span>) {
    p.name = name
}

<span style="color:#0000ff">func</span> (p *Person) GetName() <span style="color:#a31515">string</span> {
    <span style="color:#0000ff">return</span> p.name
}
</code></span></span>

这个结构体定义了一个名为Person的结构体类型,包含了两个私有的成员变量name和age,以及两个公有的方法SetName和GetName,用来设置和获取name成员变量的值。不同于其它语言使用Public,Private定义公有和私有,Go使用编程规范来定义这个概念。变量名首字母大写代表公有,对外可见;变量名首字母小写代表私有,对外不可见。(经过实验,上面的说法是有一个大前提的。同一个包内,无论是公有变量还是私有变量,在任何地方都可以访问!!!!,只有在不同的包里,才有上面变量名大小写来控制可见性的说法。😣😣😣)Go的变量命名主要使用驼峰命名法,也算是约定俗成吧。

  • 继承和组合
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">type</span> Person <span style="color:#0000ff">struct</span> {
    name <span style="color:#a31515">string</span>
    age <span style="color:#a31515">int</span>
}

<span style="color:#0000ff">type</span> Student <span style="color:#0000ff">struct</span> {
    Person  <span style="color:#008000">// 匿名嵌套Person结构体</span>
    id <span style="color:#a31515">string</span>
}

<span style="color:#0000ff">func</span> (s *Student) SetId(id <span style="color:#a31515">string</span>) {
    s.id = id
}
</code></span></span>

这个结构体定义了一个名为Student的结构体类型,通过匿名嵌套Person结构体,实现了从Person结构体继承了name和age成员变量和方法,并添加了一个id成员变量和SetId方法。这样,我们就可以通过Student结构体来访问和操作Person结构体的成员变量和方法。匿名嵌套是继承,不匿名就是组合的使用方法了。

  • 接口多态

声明一个Shape类型的接口,该接口里定义了Area()函数。Rectangle和Circle实现了Shape类型接口里的Area()的方法,可以认定为是一个实现类。PrintArea方法接受一个Shape类型的数据,然后输出面积。这个形参是Shape类型,因此,就有了一个“向上转型”的效果。

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"math"</span>
)

<span style="color:#0000ff">type</span> Shape <span style="color:#0000ff">interface</span> {
	Area() <span style="color:#a31515">float64</span>
}

<span style="color:#0000ff">type</span> Rectangle <span style="color:#0000ff">struct</span> {
	Width  <span style="color:#a31515">float64</span>
	Height <span style="color:#a31515">float64</span>
}

<span style="color:#0000ff">func</span> (r Rectangle) Area() <span style="color:#a31515">float64</span> {
	<span style="color:#0000ff">return</span> r.Width * r.Height
}

<span style="color:#0000ff">type</span> Circle <span style="color:#0000ff">struct</span> {
	Radius <span style="color:#a31515">float64</span>
}

<span style="color:#0000ff">func</span> (c Circle) Area() <span style="color:#a31515">float64</span> {
	<span style="color:#0000ff">return</span> math.Pi * c.Radius * c.Radius
}

<span style="color:#0000ff">func</span> <span style="color:#a31515">PrintArea</span>(s Shape) {
	fmt.Println(s.Area())
}

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	r := Rectangle{Width: <span style="color:#880000">3</span>, Height: <span style="color:#880000">4</span>}
	c := Circle{Radius: <span style="color:#880000">5</span>}

	PrintArea(r) <span style="color:#008000">// 输出 12</span>
	PrintArea(c) <span style="color:#008000">// 输出 78.53981633974483</span>
}
</code></span></span>
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-go"><span style="color:#0000ff">package</span> main

<span style="color:#0000ff">import</span> (
	<span style="color:#a31515">"fmt"</span>
	<span style="color:#a31515">"math"</span>
)

<span style="color:#0000ff">type</span> coordinate <span style="color:#0000ff">struct</span> {
	x, y     <span style="color:#a31515">int</span>
	isMarked <span style="color:#a31515">bool</span>
}

<span style="color:#0000ff">func</span> <span style="color:#a31515">distence</span>(x1 <span style="color:#a31515">int</span>, y1 <span style="color:#a31515">int</span>, x2 <span style="color:#a31515">int</span>, y2 <span style="color:#a31515">int</span>) <span style="color:#a31515">float64</span> {
	<span style="color:#0000ff">return</span> math.Sqrt(math.Pow(<span style="color:#a31515">float64</span>(x1-x2), <span style="color:#880000">2</span>) + math.Pow(<span style="color:#a31515">float64</span>(y1-y2), <span style="color:#880000">2</span>))
}

<span style="color:#0000ff">func</span> <span style="color:#a31515">main</span>() {
	<span style="color:#0000ff">var</span> n, k, t <span style="color:#a31515">int</span>
	fmt.Scan(&n, &k, &t)
	coordinates := <span style="color:#0000ff">make</span>([]coordinate, n)
	<span style="color:#0000ff">for</span> i := <span style="color:#880000">0</span>; i < n; i++ {
		fmt.Scan(&coordinates[i].x, &coordinates[i].y)
	}

	<span style="color:#0000ff">for</span> i := <span style="color:#880000">0</span>; i < k; i++ {
		<span style="color:#0000ff">var</span> x, y <span style="color:#a31515">int</span>
		fmt.Scan(&x, &y)
		<span style="color:#0000ff">for</span> j := <span style="color:#880000">0</span>; j < n; j++ {
			<span style="color:#0000ff">if</span> x == coordinates[j].x && y == coordinates[j].y {
				coordinates[j].isMarked = <span style="color:#a31515">true</span>
				<span style="color:#0000ff">break</span>
			}
		}
	}

	<span style="color:#008000">// 记录最远距离的坐标,以及最远距离</span>
	<span style="color:#0000ff">var</span> maxDistence <span style="color:#a31515">float64</span> = <span style="color:#880000">0.0</span>
	<span style="color:#0000ff">var</span> maxDistenceid <span style="color:#a31515">int</span> = <span style="color:#880000">-1</span>
	<span style="color:#0000ff">var</span> res <span style="color:#a31515">int</span> = <span style="color:#880000">0</span>
	<span style="color:#0000ff">for</span> i := <span style="color:#880000">0</span>; i < t; i++ {
		<span style="color:#0000ff">var</span> x, y <span style="color:#a31515">int</span>
		fmt.Scan(&x, &y)
		<span style="color:#0000ff">for</span> j := <span style="color:#880000">0</span>; j < n; j++ {
			<span style="color:#0000ff">if</span> distence(x, y, coordinates[j].x, coordinates[j].y) > maxDistence {
				<span style="color:#008000">// fmt.Println(x, y, coordinates[j].x, coordinates[j].y)</span>
				<span style="color:#008000">// fmt.Println("distence:", distence(x, y, coordinates[j].x, coordinates[j].y))</span>
				maxDistence = distence(x, y, coordinates[j].x, coordinates[j].y)
				maxDistenceid = j
			}
		}
		<span style="color:#0000ff">if</span> coordinates[maxDistenceid].isMarked {
			res++
		}
		<span style="color:#008000">// 更新最远距离</span>
		maxDistence = <span style="color:#880000">0.0</span>
		maxDistenceid = <span style="color:#880000">-1</span>
	}
	fmt.Println(res)
}
</code></span></span>

补充

函数的内容并没有专门拎出来讲是两个原因。第一个原因是有编程基础的人瞄一眼语法就会用了,不太需要刻意的写。第二个原因是每部分的代码都或多或少的用到了函数,不用解释也能看得懂。当然,函数要讲起来还是比较多的,比如传值和传址,数组指针,指针数组这种,这确实是重难点。如果有机会的话,我可能会专门去总结这部分的内容。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野生的狒狒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值