Merge branch 'master' into master
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
### 这篇讲义讲什么
|
||||
|
||||
- 首先,如上文所述,如何轻松地利用本章乃至整个讲义
|
||||
- 在第一点的基础上,引申出我自己归纳的<strong>编程入门之“道”</strong>
|
||||
- 在第一点的基础上,引申出我自己归纳的**编程入门之“道”**
|
||||
|
||||
### 请随意喷这篇讲义
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
1. 这里的文章的最大的作用是帮你打开信息壁垒,告诉你编程的世界有哪些东西,可以去学什么。
|
||||
2. 把讲义当成字典来用。很多文章并不是完全对新人友好的,你现在不需要看懂,甚至可能不需要看。你只要大概记下有这么个概念和工具,当下次要用到的时候,再查阅、再仔细学习就好。
|
||||
|
||||
简单来说就是,<strong>抱着平和的心态,随便看看</strong>,知道这一章都讲了哪些东西,看完有个印象就好,然后常回家看看。
|
||||
简单来说就是,**抱着平和的心态,随便看看**,知道这一章都讲了哪些东西,看完有个印象就好,然后常回家看看。
|
||||
|
||||
技术细节本身永远都不是最重要的,重要的是思想和方法,如何快速掌握一门技术。
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
这是我的第一个也是最重要的建议。
|
||||
|
||||
无论是学一门语言,还是学一个工具:<strong>尽可能地先用最短的时间搞懂这个东西是做什么的,然后以最快的方式把它“run”起来。</strong>
|
||||
无论是学一门语言,还是学一个工具:**尽可能地先用最短的时间搞懂这个东西是做什么的,然后以最快的方式把它“run”起来。**
|
||||
|
||||
当你已经能跑起一个语言、一个工具的最简单的示例的时候,再去花时间慢慢了解背后的复杂的内容,再去拓展即可。先用起来,跑起来,带着问题去翻资料。
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
那么该怎么学呢?
|
||||
|
||||
<strong>先简单地会一样东西的最核心的部分,再去找一个实际的编程场景、编程任务、项目。你会在完成这个项目中遇到各种各样的问题,无论是遗漏了知识点还是压根没思路,这时候不断地用搜索引擎来学习。( </strong>[2.3 高效的信息检索](../2.%E9%AB%98%E6%95%88%E5%AD%A6%E4%B9%A0/2.3%E9%AB%98%E6%95%88%E7%9A%84%E4%BF%A1%E6%81%AF%E6%A3%80%E7%B4%A2.md)<strong>)</strong>
|
||||
**先简单地会一样东西的最核心的部分,再去找一个实际的编程场景、编程任务、项目。你会在完成这个项目中遇到各种各样的问题,无论是遗漏了知识点还是压根没思路,这时候不断地用搜索引擎来学习。( **[2.3 高效的信息检索](../2.%E9%AB%98%E6%95%88%E5%AD%A6%E4%B9%A0/2.3%E9%AB%98%E6%95%88%E7%9A%84%E4%BF%A1%E6%81%AF%E6%A3%80%E7%B4%A2.md)**)**
|
||||
|
||||
举个例子:你想做一个小程序,来检测某电影院的电影预售。程序大概要做到不断刷新网页,一检测到这个电影预售了,就马上发短信给自己手机(或者直接帮你抢)
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
刚开始你可能什么都不会,什么地方都被阻塞,但当你把坑踩遍了。就发现,哎嘿,不好意思,这玩意我怎么又会!
|
||||
|
||||
<strong>所以让我们基于这个“任务驱动”,再看看本章的内容。这些内容大多看了就忘,因为细节非常多,而且并不一定能解决你手头上的问题。但这些文档,带你领进了新的领域的大门,让你的工具箱里多了一个可以解决问题的工具,以后用到了可以想起他们。并且,这些文章多是通俗的,且作者多是讲述了 ta 所认为的该语言/工具的最核心、最精华的部分,或者说第一次入门最需要学习的部分。</strong>
|
||||
**所以让我们基于这个“任务驱动”,再看看本章的内容。这些内容大多看了就忘,因为细节非常多,而且并不一定能解决你手头上的问题。但这些文档,带你领进了新的领域的大门,让你的工具箱里多了一个可以解决问题的工具,以后用到了可以想起他们。并且,这些文章多是通俗的,且作者多是讲述了 ta 所认为的该语言/工具的最核心、最精华的部分,或者说第一次入门最需要学习的部分。**
|
||||
|
||||
## 圈子
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
当然在这里我们主要讲的是代码编辑器,一个好的编辑器可以节省开发时间,提高工作效率,它们都能提供非常方便易用的开发环境。你可以用它们来编写代码,查看源文件和文档等,简化你的工作。以下是一些常用的代码编辑器,每个不同的编辑器都有不尽相同的目标用户群体。
|
||||
|
||||
- <em>Visual Studio Code</em> : 微软 VS 系列的新作品,适用于多平台的代码编辑器,其很好服从了轻量化 + 拓展的 Unix 思想,在整体快捷方便的同时具有极强的功能拓展空间,是值得首要推荐的编辑器。
|
||||
- <em>Vim </em>: Vim 是从 vi 发展出来的一个文本编辑器,在程序员中被广泛使用,运行在 Linux 环境下。
|
||||
- <em>GNU Emacs</em> : Emacs 是一个轻便、可扩展、免费的编辑器,它比其它的编辑器要更强大,是一个整合环境,或可称它为集成开发环境。它可以处理文字,图像,高亮语法,将代码更直观地展现给开发者。
|
||||
- *Visual Studio Code* : 微软 VS 系列的新作品,适用于多平台的代码编辑器,其很好服从了轻量化 + 拓展的 Unix 思想,在整体快捷方便的同时具有极强的功能拓展空间,是值得首要推荐的编辑器。
|
||||
- *Vim*: Vim 是从 vi 发展出来的一个文本编辑器,在程序员中被广泛使用,运行在 Linux 环境下。
|
||||
- *GNU Emacs* : Emacs 是一个轻便、可扩展、免费的编辑器,它比其它的编辑器要更强大,是一个整合环境,或可称它为集成开发环境。它可以处理文字,图像,高亮语法,将代码更直观地展现给开发者。
|
||||
|
||||
### 什么是编译器
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ author:wenjing
|
||||
|
||||
## ACM 能为我带来什么?
|
||||
|
||||
显然,做为一名计算机专业的学生,编程是一项必须掌握的技能。再次引用 Niklaus Emil Wirth 的一句话:<strong>程序=算法 + 数据结构。</strong>例如在大一开设的程序设计基础中,我们需要重点学习链表这一数据结构,熟悉运用分支与循环结构(勉强也算算法吧)。然而,在 ACM 中,这是基础到不值一提的事物,宛如空气与水一般基础。你们是否想过,花了大量课时学习的这些知识,其实小学生也可以学会(看看远处的小学编程补习班吧,家人们)那做为大学生去学习这些知识,是否应当得到一些不止于考试内容的知识呢?
|
||||
显然,做为一名计算机专业的学生,编程是一项必须掌握的技能。再次引用 Niklaus Emil Wirth 的一句话:**程序=算法 + 数据结构。**例如在大一开设的程序设计基础中,我们需要重点学习链表这一数据结构,熟悉运用分支与循环结构(勉强也算算法吧)。然而,在 ACM 中,这是基础到不值一提的事物,宛如空气与水一般基础。你们是否想过,花了大量课时学习的这些知识,其实小学生也可以学会(看看远处的小学编程补习班吧,家人们)那做为大学生去学习这些知识,是否应当得到一些不止于考试内容的知识呢?
|
||||
|
||||
我认为有两个方向,一是我们去学习一些更底层的逻辑与原理,此外就是学习如何更好的利用链表,实现一些别的数据结构做不到的事情,我认为 ACM 可以极大的提升我们对后者的理解。
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ Div.1、Div.2、Div.3、Div.4 数字越小难度越大。
|
||||
这是一场笔者之前赛后补过的 Div.2,画面右下角分别为赛后公告和题解,右侧便是开启 VP 的按钮。
|
||||

|
||||
|
||||
<em>VP</em><em>模拟赛时的好处就是在虚拟参赛中获得真实比赛才能积累的经验,比如这里笔者发现通过前三题后,我应该先去看看 F 题,因为做出来的人更多,我有更大的可能性做出来,ACM 中题目并不是 100% 按难度排序。</em>
|
||||
*VP模拟赛时的好处就是在虚拟参赛中获得真实比赛才能积累的经验,比如这里笔者发现通过前三题后,我应该先去看看 F 题,因为做出来的人更多,我有更大的可能性做出来,ACM 中题目并不是 100% 按难度排序。*
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
## 学了算法就相当于学好了计算机吗?
|
||||
|
||||
学好了算法当然不等于学好了计算机科学。计算机科学是一个非常庞大的知识体系,包括更为底层的计算机组成原理、编译原理等,更为表层的 AI,开发等,是一门综合性学科。总的来说,算法是计算机科学中较为重要的一部分,但<strong>远远</strong>不是全部。
|
||||
学好了算法当然不等于学好了计算机科学。计算机科学是一个非常庞大的知识体系,包括更为底层的计算机组成原理、编译原理等,更为表层的 AI,开发等,是一门综合性学科。总的来说,算法是计算机科学中较为重要的一部分,但**远远**不是全部。
|
||||
|
||||
## 学算法就要用《算法导论》一类的书吗?
|
||||
|
||||
@@ -32,6 +32,6 @@ ACM 是美国计算机协会(Association for Computing Machinery)的缩写
|
||||
|
||||
在我校,参加 ACM 社团(姑且叫做社团)并不代表能够参加有含金量的团体赛(ICPC、CCPC 等)。你需要先参加由我校教练刘春英老师组织的各种比赛,有资格进入集训队后,才有机会代表学校参加比赛(当然不限名额的个人赛想参加就参加)。
|
||||
|
||||
进入集训队后采取末位淘汰制度(最后留下来的人在 20 人左右),最后留下来的人才有机会参加比赛。<strong>因此个人并不推荐 0 基础的同学对于 ACM 过于执着</strong>,有 0 基础的同学最后进入校队的例子,不过这通常意味着你一天至少得刷一道算法题。如果还是想尝试的同学,可以去洛谷 ([www.luogu.com.cn](http://www.luogu.com.cn))、Codeforces([www.codeforces.com](http://www.codeforces.com))、Atcoder([atcoder.jp](https://atcoder.jp/)) 等平台上注册账号,练习题目,参加这些网站定期组织的一些比赛。
|
||||
进入集训队后采取末位淘汰制度(最后留下来的人在 20 人左右),最后留下来的人才有机会参加比赛。**因此个人并不推荐 0 基础的同学对于 ACM 过于执着**,有 0 基础的同学最后进入校队的例子,不过这通常意味着你一天至少得刷一道算法题。如果还是想尝试的同学,可以去洛谷 ([www.luogu.com.cn](http://www.luogu.com.cn))、Codeforces([www.codeforces.com](http://www.codeforces.com))、Atcoder([atcoder.jp](https://atcoder.jp/)) 等平台上注册账号,练习题目,参加这些网站定期组织的一些比赛。
|
||||
|
||||
如果经过一段时间的练习能够在 Codefoces([www.codeforces.com](http://www.codeforces.com))上达到 1400 以上的 Rating,那么可以再观望观望参与 ACM。
|
||||
|
||||
@@ -22,9 +22,9 @@ C 语言其实是一门优秀的承上启下的语言,既具有高级语言的
|
||||
|
||||
但是其功能毕竟受限,有时候用起来会苦恼其操作受限以及各种奇奇怪怪的 bug 问题。
|
||||
|
||||
<strong>如果为了增强自身的编程能力和计算机素养,培养解决问题的能力,C 语言的你的不二选择。在这里强烈推荐 jyy 老师的各类课程。(</strong><strong>[http://jyywiki.cn/](http://jyywiki.cn/)</strong><strong>)</strong>
|
||||
**如果为了增强自身的编程能力和计算机素养,培养解决问题的能力,C 语言的你的不二选择。在这里强烈推荐 jyy 老师的各类课程。([http://jyywiki.cn/](http://jyywiki.cn/))**
|
||||
|
||||
<strong>我们的任务一部分会使用 C 语言,一方面培养大家编程能力,一方面辅助大家期末考试。</strong>
|
||||
**我们的任务一部分会使用 C 语言,一方面培养大家编程能力,一方面辅助大家期末考试。**
|
||||
|
||||
### C++
|
||||
|
||||
@@ -34,7 +34,7 @@ C 语言其实是一门优秀的承上启下的语言,既具有高级语言的
|
||||
- 更高级的语言特征,可自定义数据类型
|
||||
- 标准库
|
||||
|
||||
<strong>C++ 既有 C 面向过程的特点,又拥有面向对象的特性,是一门系统级的语言。</strong>
|
||||
**C++ 既有 C 面向过程的特点,又拥有面向对象的特性,是一门系统级的语言。**
|
||||
|
||||
编译器、操作系统的开发,高性能服务器的开发,游戏引擎的开发,硬件编程,深度学习框架的开发......只要是和底层系统或者是与性能相关的事情,通常都会有 C++ 的一席之地。
|
||||
|
||||
@@ -46,7 +46,7 @@ Python 在图里是电锯,适合干比较“狂野”的任务,也是深度
|
||||
|
||||
使用缩进控制语句是此语言的特点。
|
||||
|
||||
<strong>作为深度学习的主要使用语言,我们将以</strong><strong>P</strong><strong>ython 为主。</strong>
|
||||
**作为深度学习的主要使用语言,我们将以****P****ython 为主。**
|
||||
|
||||
## JAVA
|
||||
|
||||
@@ -54,7 +54,7 @@ Python 在图里是电锯,适合干比较“狂野”的任务,也是深度
|
||||
|
||||
他太老了,虽然不少框架都依托于 Java,但是不得不说,一些地方略有落后。
|
||||
|
||||
<strong>频繁应用于</strong><strong>W</strong><strong>eb 开发,安卓应用等等。</strong>
|
||||
**频繁应用于****W****eb 开发,安卓应用等等。**
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
更严重的是,他可能会透支学校的信誉。
|
||||
|
||||
<strong>同时,先学好 C 语言对你有以下帮助:</strong>
|
||||
**同时,先学好 C 语言对你有以下帮助:**
|
||||
|
||||
1. 掌握计算机底层知识:C 语言是一种高效的系统级语言,它的语法和数据结构设计直接映射到底层计算机硬件,通过学习 C 语言可以更深入地了解计算机底层运作原理,为理解更高级的编程语言和开发工具奠定基础。
|
||||
2. 提高编程能力:C 语言的语法相对较为简单,但是它要求程序员手动管理内存,这需要编程者深入了解内存结构和指针的使用。通过学习 C 语言,可以锻炼编程能力,提高代码质量和效率。
|
||||
@@ -65,7 +65,7 @@ NJU-ICS-PA 南京大学计算机系统基础
|
||||
|
||||
有且仅有大学有这样好的资源帮助你了
|
||||
|
||||
## <strong>坚持了好久还是搞不定,我想放弃了</strong>
|
||||
## **坚持了好久还是搞不定,我想放弃了**
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Visual Studio(以下简称 VS)是 Windows 下最完美的 C/C++ 等语言的
|
||||
|
||||
什么是 IDE,什么是代码编辑器,什么是编译器等等细碎问题参考文档 [3.1 该使用哪个编辑器???](3.1%E8%AF%A5%E4%BD%BF%E7%94%A8%E5%93%AA%E4%B8%AA%E7%BC%96%E8%BE%91%E5%99%A8%EF%BC%9F%EF%BC%9F%EF%BC%9F.md) 看不懂的话直接无脑装
|
||||
|
||||
### <strong>下载</strong>
|
||||
### **下载**
|
||||
|
||||
[https://visualstudio.microsoft.com/zh-hans/downloads/](https://visualstudio.microsoft.com/zh-hans/downloads/)
|
||||
|
||||
@@ -60,7 +60,7 @@ VS 是项目制,你需要创建一个项目才能开始编写代码并运行
|
||||
|
||||
阅读完以后,就可以将代码全部删去,编写自己的代码了。
|
||||
|
||||
注意控制台项目初始源文件后缀为.cpp 为 C++ 文件,如果编写 C 语言<strong>建议将后缀改为.c</strong>。.cpp 存在隐患:如果不小心使用了 C++ 的语法而非 C 存在的语法,编译器并不会报错,且 C 与 C++ 在某些特性存在区别。
|
||||
注意控制台项目初始源文件后缀为.cpp 为 C++ 文件,如果编写 C 语言**建议将后缀改为.c**。.cpp 存在隐患:如果不小心使用了 C++ 的语法而非 C 存在的语法,编译器并不会报错,且 C 与 C++ 在某些特性存在区别。
|
||||
|
||||
### “运行”你的 C 语言代码
|
||||
|
||||
@@ -88,7 +88,7 @@ IDE 相比于代码编辑器,最强大的一点莫过于成熟的调试系统
|
||||
|
||||

|
||||
|
||||
### <strong>深色主题</strong>
|
||||
### **深色主题**
|
||||
|
||||
需要深色主题请在工具 - 主题里更改为深色
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
## Work an Example Yourself
|
||||
|
||||
尝试设计算法的第一步是<strong>自己(手动)处理至少一个问题实例,为每个参数选择特定值。</strong>往往需要确定<strong>一个正确的示例,以及错误的示例。</strong>
|
||||
尝试设计算法的第一步是**自己(手动)处理至少一个问题实例,为每个参数选择特定值。**往往需要确定**一个正确的示例,以及错误的示例。**
|
||||
|
||||
如果你在这一步陷入困境,这通常意味着两件事中的一件。第一种情况是问题不明确,不清楚你应该做什么。在这种情况下,你必须在继续之前解决问题。如果你正在解决自己创造的问题,你可能需要更仔细地思考正确的答案应该是什么,并完善你对问题的定义。
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
|
||||
## Write Down What You Just Did
|
||||
|
||||
这一步中,必须思考解决问题所做的工作,并写下<strong>解决该特定实例的步骤。</strong>思考这一步骤的另一种方式是,写下一组清晰的指示,<strong>其他人可以按照这些指示来重现刚刚解决的特定问题实例的答案</strong>。如果在步骤 1 中执行了多个实例,那么也将重复步骤 2 多次,对步骤 1 中的每个实例重复一次。如果一条指令有点复杂,那没关系,只要指令稍后有明确的含义,我们将把这些复杂的步骤转化为它们自己的编程问题,这些问题将单独解决。
|
||||
这一步中,必须思考解决问题所做的工作,并写下**解决该特定实例的步骤。**思考这一步骤的另一种方式是,写下一组清晰的指示,**其他人可以按照这些指示来重现刚刚解决的特定问题实例的答案**。如果在步骤 1 中执行了多个实例,那么也将重复步骤 2 多次,对步骤 1 中的每个实例重复一次。如果一条指令有点复杂,那没关系,只要指令稍后有明确的含义,我们将把这些复杂的步骤转化为它们自己的编程问题,这些问题将单独解决。
|
||||
|
||||
## Generalize Your Steps
|
||||
|
||||
<strong>将步骤 2 得到的具体步骤,抽象为一般性的结论。</strong>有时可能很难概括步骤。发生这种情况时,返回步骤 1 和 2 可能会有所帮助。做更多的问题实例将提供更多的信息供参考,更能帮助深入算法。这个过程通常被称为编写“伪代码”,以编程方式设计算法,而不使用特定的目标语言。几乎所有的程序员在编写任何实际代码之前都会使用这种方法来确保他们的算法是正确的。
|
||||
**将步骤 2 得到的具体步骤,抽象为一般性的结论。**有时可能很难概括步骤。发生这种情况时,返回步骤 1 和 2 可能会有所帮助。做更多的问题实例将提供更多的信息供参考,更能帮助深入算法。这个过程通常被称为编写“伪代码”,以编程方式设计算法,而不使用特定的目标语言。几乎所有的程序员在编写任何实际代码之前都会使用这种方法来确保他们的算法是正确的。
|
||||
|
||||
## Test Your Algorithm
|
||||
|
||||
在步骤 3 之后,我们有了一个我们认为正确的算法。然而,我们完全有可能在这一路上搞砸了。步骤 4 的主要目的是确保我们的步骤在继续之前是正确的。为了实现这一点,我们使用<strong>不同于设计算法时使用的参数值</strong>来测试我们的算法。我们手动执行算法,并将其获得的答案与正确的答案进行比较。如果它们不同,那么我们知道我们的算法是错误的。我们使用的测试用例(参数值)越多,我们就越能确信我们的算法是正确的。不幸的是,通过测试无法确保我们的算法是正确的。要完全确定你的算法是正确的,唯一的方法就是正式证明它的正确性(使用数学证明),这超出了这个专门化的范围。
|
||||
在步骤 3 之后,我们有了一个我们认为正确的算法。然而,我们完全有可能在这一路上搞砸了。步骤 4 的主要目的是确保我们的步骤在继续之前是正确的。为了实现这一点,我们使用**不同于设计算法时使用的参数值**来测试我们的算法。我们手动执行算法,并将其获得的答案与正确的答案进行比较。如果它们不同,那么我们知道我们的算法是错误的。我们使用的测试用例(参数值)越多,我们就越能确信我们的算法是正确的。不幸的是,通过测试无法确保我们的算法是正确的。要完全确定你的算法是正确的,唯一的方法就是正式证明它的正确性(使用数学证明),这超出了这个专门化的范围。
|
||||
|
||||
确定好的测试用例是一项重要的技能,可以随着实践而提高。对于步骤 4 中的测试,您需要使用至少产生几个不同答案的情况进行测试(例如,如果您的算法有“是”或“否”答案,则应使用同时产生“是”和“否”的参数进行测试)。您还应该测试任何角落情况,其中行为可能与更一般的情况不同。每当您有条件决定(包括计算位置的限制)时,您应该在这些条件的边界附近测试潜在的角点情况。
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
在黑盒测试中,测试人员只考虑功能的预期行为,而不考虑设计测试用例的任何实现细节。缺乏对实现细节的访问是这种测试方法的由来——函数的实现被视为测试人员无法查看的“黑盒子”。
|
||||
|
||||
事实上我们无需执行步骤 1-5 就能够为假设问题设想好的测试用例。实际上,在开始解决问题之前,您可以针对问题提出一组黑盒测试。一些程序员提倡<strong>测试优先</strong>的开发方法。一个优点是,如果您在开始之前编写了一个全面的测试套件,那么在实现代码之后就不太可能在测试上有所疏漏。另一个优点是,通过提前考虑你的情况,你在开发和实现算法时能够降低错误率。
|
||||
事实上我们无需执行步骤 1-5 就能够为假设问题设想好的测试用例。实际上,在开始解决问题之前,您可以针对问题提出一组黑盒测试。一些程序员提倡**测试优先**的开发方法。一个优点是,如果您在开始之前编写了一个全面的测试套件,那么在实现代码之后就不太可能在测试上有所疏漏。另一个优点是,通过提前考虑你的情况,你在开发和实现算法时能够降低错误率。
|
||||
|
||||
- 选择测试用例的一些建议
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# C 语言前置概念学习
|
||||
|
||||
如何学习 C 语言?<strong>第一步:Throw away the textbook。</strong>也许你可以通过以下途径:
|
||||
如何学习 C 语言?**第一步:Throw away the textbook。**也许你可以通过以下途径:
|
||||
|
||||
以下方式难度由易到难,但并不意味着收获由小到大:
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
4.Web:[CNote](https://github.com/coderit666/CNote)(例子密集,学习曲线平滑,覆盖面广且具有深度)
|
||||
|
||||
5.Book:<strong>教材替换用书——《C Primer Plus》!</strong>(基础且深入的恰到好处,有一定拓展,可能后面的章节有点难懂,是一本不可多得的好书,不要忽视课本习题及 Projects)
|
||||
5.Book:**教材替换用书——《C Primer Plus》!**(基础且深入的恰到好处,有一定拓展,可能后面的章节有点难懂,是一本不可多得的好书,不要忽视课本习题及 Projects)
|
||||
|
||||
6.MOOC:[Introductory C Programming 专项课程](https://www.coursera.org/specializations/c-programming)(<strong>全英文</strong>,好处是涉及到计算机思维,包含许多常用 tools 的教学例如 git、make、emacs、gdb,视频讲解结合文档阅读,对于 C 的重要核心知识讲解透彻,难度颇高,建议用作提升)
|
||||
6.MOOC:[Introductory C Programming 专项课程](https://www.coursera.org/specializations/c-programming)(**全英文**,好处是涉及到计算机思维,包含许多常用 tools 的教学例如 git、make、emacs、gdb,视频讲解结合文档阅读,对于 C 的重要核心知识讲解透彻,难度颇高,建议用作提升)
|
||||
|
||||
7.Web:[LinuxC 一站式编程](https://akaedu.github.io/book/)(难度大,枯燥硬核,收获多,基于 linux)
|
||||
|
||||
@@ -38,4 +38,4 @@
|
||||
|
||||

|
||||
|
||||
### <strong>CS education is more than just “learning how to code”!</strong>
|
||||
### **CS education is more than just “learning how to code”!**
|
||||
|
||||
@@ -54,9 +54,9 @@ typedef struct Node* Link;
|
||||
|
||||
例如,若链表无头结点,则对于在链表中第一个数据结点之前插入一个新结点,或者对链表中第一个数据结点做删除操作,都必须要当做特殊情况,进行特殊考虑;而若链表中设有头结点,以上两种特殊情况都可被视为普通情况,不需要特殊考虑,降低了问题实现的难度。
|
||||
|
||||
<strong>链表有头结点,也不一定都是有利的。例如解决约瑟夫环问题,若链表有头结点,在一定程度上会阻碍算法的实现。</strong>
|
||||
**链表有头结点,也不一定都是有利的。例如解决约瑟夫环问题,若链表有头结点,在一定程度上会阻碍算法的实现。**
|
||||
|
||||
<strong>所以,对于一个链表来说,设置头指针是必要且必须的,但有没有头结点,则需要根据实际问题特殊分析。</strong>
|
||||
**所以,对于一个链表来说,设置头指针是必要且必须的,但有没有头结点,则需要根据实际问题特殊分析。**
|
||||
|
||||
首元结点:指的是链表开头第一个存有数据的结点。
|
||||
|
||||
@@ -329,7 +329,7 @@ int ListDelete(Link *L, int i, int* e)
|
||||
- 3 出列后,从 5 开始数 1,2 数 2,所以 2 出列;
|
||||
- 最后只剩下 5 自己,所以 5 胜出。
|
||||
|
||||
那么,究竟要如何用链表实现约瑟夫环呢?如何让一个含 5 个元素的约瑟夫环,能从第 5 个元素出发,访问到第 2 个元素呢?上面所讲的链表操作显然是难以做到的,解决这个问题就需要用到<strong>循环链表</strong>。
|
||||
那么,究竟要如何用链表实现约瑟夫环呢?如何让一个含 5 个元素的约瑟夫环,能从第 5 个元素出发,访问到第 2 个元素呢?上面所讲的链表操作显然是难以做到的,解决这个问题就需要用到**循环链表**。
|
||||
|
||||
## 循环链表
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ It is very dark in here.
|
||||
|
||||
Bye!
|
||||
|
||||
尽管可能微不足道,但该程序确实展示 <em>了</em>任何文本冒险中最重要的方面:描述性文本。一个好的故事是制作一款好的冒险游戏的要素之一。
|
||||
尽管可能微不足道,但该程序确实展示 *了*任何文本冒险中最重要的方面:描述性文本。一个好的故事是制作一款好的冒险游戏的要素之一。
|
||||
|
||||
## 为什么要用英文?
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
举个例子,一条林道可能隐藏着陷阱。虽然通道似乎从位置 A 通向位置 B,但实际上终点是位置 C,即掉进坑了。
|
||||
|
||||
假设我们的洞口被警卫挡住了。玩家就过不去,我们可以简单地将通道的<em>目的地</em>更改为终点位置(或 <em>NULL</em>),但这会导致对<em>诸如 go cave 和 look cave</em> 这样的命令做出不正确的回应:“你在这里看不到任何洞穴。我们需要一个将通道的实际终点和虚假终点分开的单独属性。为此,我们将引入一个属性 prospect 来表示后者。
|
||||
假设我们的洞口被警卫挡住了。玩家就过不去,我们可以简单地将通道的*目的地*更改为终点位置(或 *NULL*),但这会导致对*诸如 go cave 和 look cave* 这样的命令做出不正确的回应:“你在这里看不到任何洞穴。我们需要一个将通道的实际终点和虚假终点分开的单独属性。为此,我们将引入一个属性 prospect 来表示后者。
|
||||
|
||||
1. 在许多冒险中,玩家以及游戏中的 NPC 在携带量方面受到限制。给每件物品一个重量,角色库存中所有物品的总重量不应超过该角色所能承载的最大重量。当然,我们也可以给一个物体一个非常高的重量,使它不可移动(一棵树,一座房子,一座山)。
|
||||
2. RPG 式的冒险游戏需要角色的整个属性范围 ( 玩家与非玩家 ),例如 HP。HP 为零的对象要么死了,要么根本不是角色。
|
||||
@@ -120,7 +120,7 @@ extern OBJECT objs[];
|
||||
::: warning 🤔 思考题:你能否自行实现上述伪代码?
|
||||
:::
|
||||
|
||||
现在,我们已经可以使用新属性 (如果你完成了上面的思考题),<strong>details</strong> 用于新识别的命令<em>外观`<object>`</em>,<strong>textGo</strong> 在我们的命令 <em>go</em> 实现中替换固定文本<em>“OK</em>”。
|
||||
现在,我们已经可以使用新属性 (如果你完成了上面的思考题),**details** 用于新识别的命令*外观`<object>`*,**textGo** 在我们的命令 *go* 实现中替换固定文本*“OK*”。
|
||||
|
||||
## location.c
|
||||
|
||||
@@ -331,11 +331,11 @@ void executeInventory(void)
|
||||
::: warning 🤔 思考题:仔细观察这段代码,看看与你写的有何不同?
|
||||
:::
|
||||
|
||||
权重检查利用了新功能 <em>weightOfContents,</em>它将在<em>misc.c</em>中实现。在同一模块中,我们还对一些现有函数进行了修改,以支持最后几个属性。
|
||||
权重检查利用了新功能 *weightOfContents,*它将在*misc.c*中实现。在同一模块中,我们还对一些现有函数进行了修改,以支持最后几个属性。
|
||||
|
||||
属性内容将替换固定文本<em>“You see”。</em>在列出玩家的库存时,原始文本已经有点奇怪了,但是现在函数<em>listObjectsAtLocation</em>用于显示任何可能对象的内容(请参阅上面的函数<em>expertLook</em>),我们真的需要一些更灵活的东西。
|
||||
属性内容将替换固定文本*“You see”。*在列出玩家的库存时,原始文本已经有点奇怪了,但是现在函数*listObjectsAtLocation*用于显示任何可能对象的内容(请参阅上面的函数*expertLook*),我们真的需要一些更灵活的东西。
|
||||
|
||||
在函数 <em>getPassage</em> 中我们将属性<em>目标</em>替换为 prospect,并改进<em>对所有</em>命令(而不仅仅是 <em>go</em> and <em>look</em>)的响应,这些命令应用于位于“隐藏通道”另一端的位置。
|
||||
在函数 *getPassage* 中我们将属性*目标*替换为 prospect,并改进*对所有*命令(而不仅仅是 *go* and *look*)的响应,这些命令应用于位于“隐藏通道”另一端的位置。
|
||||
|
||||
## misc.h
|
||||
|
||||
@@ -462,8 +462,8 @@ function outputEdge(from, to, style)
|
||||
|
||||
注意:
|
||||
|
||||
- 尽量不要太担心浪费仅在某些类型的对象中使用的属性上的内存空间(例如,<em>textGo</em>仅用于通道),或者许多重复的字符串文本。
|
||||
- 为了演示属性 prospect 的使用,我们使洞穴无法访问。当您查看新<em>地图时,</em>这一点立即变得很明显。进入洞穴的箭头是虚线的,这意味着这是一个虚假的通道,但不是实际的通道。请放心,洞穴将在下一章重新开放。
|
||||
- 尽量不要太担心浪费仅在某些类型的对象中使用的属性上的内存空间(例如,*textGo*仅用于通道),或者许多重复的字符串文本。
|
||||
- 为了演示属性 prospect 的使用,我们使洞穴无法访问。当您查看新*地图时,*这一点立即变得很明显。进入洞穴的箭头是虚线的,这意味着这是一个虚假的通道,但不是实际的通道。请放心,洞穴将在下一章重新开放。
|
||||
- 请注意,更详细的描述往往需要一个更大的字典(更多的对象,更多的标签)。例如,命令 look silver coin 现在返回 "该硬币的正面有一只鹰"。玩家通过输入一个命令 look eagle 来查看银币,但程序并不知道鹰是什么意思 (显然这样子是不行的)。
|
||||
|
||||
输出样例
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
打开一个封闭的通道(在这里是进入洞穴)涉及到改变一些属性值:
|
||||
|
||||
- 目的地从 NULL(空地点) 变为洞穴
|
||||
- <strong>textGo</strong>从 "警卫阻止你...... "改为 "你走进山洞"
|
||||
- **textGo**从 "警卫阻止你...... "改为 "你走进山洞"
|
||||
- 在一些特殊情况下,描述和细节不需要改变。但对于一个门洞或栅栏,其中之一(或两者)通常会包含一些从 "开放 "到 "关闭 "的文字。
|
||||
|
||||
有许多方法来实现这一目标。在这里,我们将讨论一种简单、可维护和通用的方法。
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
接下来,我们引入一个名为条件的新属性,它决定了某个对象是否存在。这两个通道将被赋予互斥的条件,因此在任何时候都只能有一个存在。
|
||||
|
||||
每个条件将被实现为一个布尔函数:<strong>TRUE</strong>意味着该对象存在,<strong>FALSE</strong>意味着它不存在。
|
||||
每个条件将被实现为一个布尔函数:**TRUE**意味着该对象存在,**FALSE**意味着它不存在。
|
||||
|
||||
```c
|
||||
bool intoCaveIsOpen(void)
|
||||
@@ -281,7 +281,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
|
||||
注意:
|
||||
|
||||
1. 警卫不可能会死,所以可以说我们的条件函数中的<strong>HP</strong>是很无用的。当然,这很容易通过添加一个 kill 命令来解决,见第 20 章。
|
||||
1. 警卫不可能会死,所以可以说我们的条件函数中的**HP**是很无用的。当然,这很容易通过添加一个 kill 命令来解决,见第 20 章。
|
||||
2. 这两个条件函数是互补的;它们有资格成为重复的代码。为了消除这一点,我们可能决定让一个函数调用另一个函数(用'!'操作符来否定结果)。一个匿名函数没有(稳定的)名字,但我们可以用它的对象来指代它。我们可以用 intoCaveBlocked 的条件函数代替。
|
||||
3. 为了简单起见,条件函数没有参数。实际上,传递一个参数 OBJECT *obj 可能更好;这使得编写更多的通用条件函数成为可能,可以在多个对象中重复使用。
|
||||
4. 在理论上,任何对象都可以成为 "条件"。在下一章,你可以看到一个类似的技术被应用于此。
|
||||
|
||||
@@ -246,7 +246,7 @@ bool matchCommand(const char *src, const char *pattern)
|
||||
|
||||
我们调整各种命令的实现,以利用新的数组参数。
|
||||
|
||||
## <strong>inventory.h</strong>
|
||||
## **inventory.h**
|
||||
|
||||
```c
|
||||
extern bool executeGet(void);
|
||||
@@ -256,7 +256,7 @@ extern bool executeGive(void);
|
||||
extern bool executeInventory(void);
|
||||
```
|
||||
|
||||
## <strong>inventory.c</strong>
|
||||
## **inventory.c**
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 2.探索未知
|
||||
|
||||
::: tip <font size=5><strong> 驾驭项目,而不是被项目驾驭</strong></font>
|
||||
::: tip <font size=5>**驾驭项目,而不是被项目驾驭**</font>
|
||||
|
||||
你和一个项目的关系会经历 4 个阶段:
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
:::
|
||||
下面的代码示例包含三个函数,每个步骤一个函数:
|
||||
|
||||
1. 函数<em>getInput</em>。
|
||||
2. 函数<em>parseAndExecute</em>。
|
||||
3. 函数<em>main</em>,负责重复调用其他两个函数。
|
||||
1. 函数*getInput*。
|
||||
2. 函数*parseAndExecute*。
|
||||
3. 函数*main*,负责重复调用其他两个函数。
|
||||
|
||||
## main.c
|
||||
|
||||
@@ -70,7 +70,7 @@ int main()
|
||||
::: warning 🤔 思考题:static 是什么意思?我为什么要用他?
|
||||
:::
|
||||
|
||||
## <strong>parsexec.h</strong>
|
||||
## **parsexec.h**
|
||||
|
||||
```c
|
||||
extern bool parseAndExecute(char *input);
|
||||
@@ -86,7 +86,7 @@ extern 是干什么的?.h 文件又在干嘛?
|
||||
在这里用指针是为了传参的时候可以传字符串哦
|
||||
:::
|
||||
|
||||
## <strong>parsexec.c</strong>
|
||||
## **parsexec.c**
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -126,9 +126,9 @@ bool parseAndExecute(char *input)
|
||||
|
||||
你的编译器可能会给出警告 the unused variable‘noun’,这些不用担心,将会在下一章解决。
|
||||
|
||||
返回<em>false</em>将导致主循环结束。
|
||||
返回*false*将导致主循环结束。
|
||||
|
||||
::: warning <font size=5><strong>RTFM&&STFW</strong></font>
|
||||
::: warning <font size=5>**RTFM&&STFW**</font>
|
||||
搞懂 strtok 和 strcmp 的用法
|
||||
:::
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ bx = torch.cat((xs[0], bs[0], xs[1], bs[1], xs[2], bs[2], xs[3], bs[3], xs[4], b
|
||||
这个故事折射出,大公司中程序员的编程习惯也许不比你好多少,他们也会写出 Copy-Paste 这种难以维护的代码。但反过来说,重视编码风格这些企业看中的能力,你从现在就可以开始培养。
|
||||
:::
|
||||
|
||||
<em>传统上,文本冒险是由(许多)不同位置组成的虚拟世界。虽然这不是必需的(一些冒险发生在一个房间里!),但这是解释</em><em>数据结构</em><em>使用的好方法。</em>
|
||||
*传统上,文本冒险是由(许多)不同位置组成的虚拟世界。虽然这不是必需的(一些冒险发生在一个房间里!),但这是解释**数据结构**使用的好方法。*
|
||||
|
||||
我们首先定义一个[结构](http://en.wikipedia.org/wiki/Struct_(C_programming_language))来表示一个位置。它包含两个简单的属性开始(稍后可能会有更多的属性)。
|
||||
|
||||
@@ -136,7 +136,7 @@ struct location locs[2] = {
|
||||
};
|
||||
```
|
||||
|
||||
让我们把它付诸实践。在上一章(<em>parsexec.c)</em> 的代码示例中,我们更改了第 4、18 和 22 行)。
|
||||
让我们把它付诸实践。在上一章(*parsexec.c)* 的代码示例中,我们更改了第 4、18 和 22 行)。
|
||||
|
||||
## <strong>parsexec.c</strong>
|
||||
|
||||
@@ -237,9 +237,9 @@ void executeGo(const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
在 C 语言中,你可以使用单个语句来定义类型(<em>结构位置</em>),声明变量(<em>locs</em>)并用其初始值填充它。
|
||||
在 C 语言中,你可以使用单个语句来定义类型(*结构位置*),声明变量(*locs*)并用其初始值填充它。
|
||||
|
||||
思考题:变量<em>locs</em>是[静态分配的](http://en.wikipedia.org/wiki/Static_memory_allocation),什么是静态分配?
|
||||
思考题:变量*locs*是[静态分配的](http://en.wikipedia.org/wiki/Static_memory_allocation),什么是静态分配?
|
||||
|
||||
静态分配和动态分配有什么不同之处?
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# 4.创建对象
|
||||
|
||||
<em>在我们继续之前,我</em><em>们</em><em>在这里使用的是</em><em>[哲学意义上](https://en.wikipedia.org/wiki/Object_(philosophy))</em><em>的“对象”一词。它与</em><em>[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)</em><em>无关,也与</em><em>Java</em><em>,</em><em>C#</em><em>和</em><em>Python</em><em>等编程语言中预定义的“对象”类型没有任何共同之处。下面,我将定义一个名为 object 的</em><em>结构体。</em>
|
||||
*在我们继续之前,我们在这里使用的是[哲学意义上](https://en.wikipedia.org/wiki/Object_(philosophy))的“对象”一词。它与[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)无关,也与Java,C#和Python等编程语言中预定义的“对象”类型没有任何共同之处。下面,我将定义一个名为 object 的结构体。*
|
||||
|
||||
冒险游戏中的大多数谜题都围绕着<strong>物品</strong>。例子:
|
||||
冒险游戏中的大多数谜题都围绕着**物品**。例子:
|
||||
|
||||
- 必须找到一把钥匙,然后用来解锁某扇门。
|
||||
- 必须杀死守卫或者诱骗守卫才能开启房间
|
||||
|
||||
所以,为了表示这个物品,我们可以使用如下[结构](http://en.wikipedia.org/wiki/Struct_(C_programming_language)):
|
||||
|
||||
- <strong>description: </strong><strong>对物品的描述</strong>
|
||||
- <strong>tag: </strong><strong>物品的类型</strong>
|
||||
- <strong>location: </strong><strong>物品所在</strong><strong>的位置。这是</strong><strong>对应</strong><strong>上一章中定义的</strong><strong>物品</strong><strong>位置</strong><strong>的指针。</strong>
|
||||
- **description: ****对物品的描述**
|
||||
- **tag: ****物品的类型**
|
||||
- **location: ****物品所在****的位置。这是****对应****上一章中定义的****物品****位置****的指针。**
|
||||
|
||||
```c
|
||||
struct object {
|
||||
@@ -87,30 +87,30 @@ for (obj = objs; obj < objs + 5; obj++)
|
||||
|
||||
那么,我们有合并这个物品(或地点)列表有什么好处呢?答案是这会让我们的代码变得更加简单,因为许多函数(如上面的函数通过这样的列表)只需要扫描单个列表就可以实现,而不是三个列表。有人可能会说没必要,因为每个命令仅适用于一种类型的对象:
|
||||
|
||||
- 命令 <em>go</em> 适用于位置对象。
|
||||
- 命令 <em>get</em> 应用于获得物品。
|
||||
- 命令 *go* 适用于位置对象。
|
||||
- 命令 *get* 应用于获得物品。
|
||||
- 命令 kill 适应用于杀死人物。
|
||||
|
||||
但这种方法不太对劲,原因有三:
|
||||
|
||||
1. 某些命令适用于多种类型的对象,尤其是<em>检查</em>。
|
||||
1. 某些命令适用于多种类型的对象,尤其是*检查*。
|
||||
2. 有时候会出现很没意思的交互方式,比如说你要吃掉守卫,他说不行。
|
||||
3. 某些对象在游戏中可能具有多个角色。比如说队友系统,NPC 可以是你的物品也可以是对象
|
||||
|
||||
将所有对象放在一个大列表中,很容易添加一个名为“type”的属性来<em>构造对象</em>,以帮助我们区分不同类型的对象。
|
||||
将所有对象放在一个大列表中,很容易添加一个名为“type”的属性来*构造对象*,以帮助我们区分不同类型的对象。
|
||||
|
||||
::: warning 🤔 怎么做怎么遍历呢?先思考吧
|
||||
:::
|
||||
|
||||
但是,对象通常具有同样有效的其他特征:
|
||||
|
||||
- <strong>Locations:通过</strong><strong>道路</strong><strong>连接(将在后面介绍)。如果一个物体无法通过一条通道到达,那么它就不是一个位置。就是这么简单。</strong>
|
||||
- <strong>Items:玩家唯一可以捡起的物品;</strong><strong>可以给他们整一个重量的属性</strong>
|
||||
- <strong>Actors:玩家唯一可以与之交谈,交易,战斗的对象;当然,前提是他们还活着!</strong><strong>可以加一个 HP 属性</strong>
|
||||
- **Locations:通过****道路****连接(将在后面介绍)。如果一个物体无法通过一条通道到达,那么它就不是一个位置。就是这么简单。**
|
||||
- **Items:玩家唯一可以捡起的物品;****可以给他们整一个重量的属性**
|
||||
- **Actors:玩家唯一可以与之交谈,交易,战斗的对象;当然,前提是他们还活着!****可以加一个 HP 属性**
|
||||
|
||||
我们还要向数组中添加一个对象:玩家自己。
|
||||
|
||||
在上一章中,有一个单独的变量 <em>locationOfPlayer</em>。我们将删除它,然后换上用户的位置属性取代他!
|
||||
在上一章中,有一个单独的变量 *locationOfPlayer*。我们将删除它,然后换上用户的位置属性取代他!
|
||||
|
||||
例如,此语句会将玩家移入洞穴:
|
||||
|
||||
@@ -163,7 +163,7 @@ OBJECT objs[] = {
|
||||
};
|
||||
```
|
||||
|
||||
<strong>注意:</strong>要编译此模块,编译器<em>必须</em>支持 Constant folding。这排除了一些更原始的编译器,如 [Z88DK](http://en.wikipedia.org/wiki/Z88DK)。
|
||||
**注意:**要编译此模块,编译器*必须*支持 Constant folding。这排除了一些更原始的编译器,如 [Z88DK](http://en.wikipedia.org/wiki/Z88DK)。
|
||||
|
||||
以下模块将帮助我们找到与指定名词匹配的对象。
|
||||
|
||||
@@ -173,7 +173,7 @@ OBJECT objs[] = {
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
```
|
||||
|
||||
::: warning <font size=5><strong>🤔 指针?函数?希望你已经掌握这是什么了</strong></font>
|
||||
::: warning <font size=5>**🤔 指针?函数?希望你已经掌握这是什么了**</font>
|
||||
:::
|
||||
|
||||
## noun.c
|
||||
@@ -232,7 +232,7 @@ OBJECT *getVisible(const char *intention, const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
这是另一个辅助程序的函数。它打印存在于特定位置的对象(物品,NPC)的列表。它将用于函数 <em>executeLook</em>,在下一章中,我们将介绍另一个需要它的命令。
|
||||
这是另一个辅助程序的函数。它打印存在于特定位置的对象(物品,NPC)的列表。它将用于函数 *executeLook*,在下一章中,我们将介绍另一个需要它的命令。
|
||||
|
||||
## misc.h
|
||||
|
||||
@@ -268,7 +268,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
}
|
||||
```
|
||||
|
||||
在 <em>location.c</em> 中,命令环<em>顾四周的实现</em>,并根据新的数据结构进行调整。旧的位置数组被删除,变量 <em>locationOfPlayer</em> 也是如此。
|
||||
在 *location.c* 中,命令环*顾四周的实现*,并根据新的数据结构进行调整。旧的位置数组被删除,变量 *locationOfPlayer* 也是如此。
|
||||
|
||||
## location.h
|
||||
|
||||
@@ -302,7 +302,7 @@ void executeLook(const char *noun)
|
||||
|
||||
void executeGo(const char *noun)
|
||||
{
|
||||
//消除了函数<em>executeGo</em>中的循环,代码更优雅了~
|
||||
//消除了函数*executeGo*中的循环,代码更优雅了~
|
||||
OBJECT *obj = getVisible("where you want to go", noun);
|
||||
if (obj == NULL)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
| 玩家从演员那里收到物品 | ask | silver->location = player; |
|
||||
| 列出其他演员的库存 | examine | listObjectsAtLocation(guard); |
|
||||
|
||||
你可以尝试去使用这些命令(上面的前两个示例已经在上一章中实现了)。现在,我们将为玩家和非玩家角色介绍一些典型的<strong>物品栏</strong>操作(命令<em>获取</em>、<em>掉落</em>、<em>给予</em>、<em>询问</em>和<em>物品栏</em>)。
|
||||
你可以尝试去使用这些命令(上面的前两个示例已经在上一章中实现了)。现在,我们将为玩家和非玩家角色介绍一些典型的**物品栏**操作(命令*获取*、*掉落*、*给予*、*询问*和*物品栏*)。
|
||||
|
||||
::: warning 🤔 思考题:
|
||||
你能不能尝试自己实现一下上面的命令?
|
||||
@@ -32,7 +32,7 @@
|
||||
如果你可以在不参考下面内容的情况下就写出基本内容会有很大收获的
|
||||
:::
|
||||
|
||||
## <strong>parsexec.c</strong>
|
||||
## **parsexec.c**
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -91,7 +91,7 @@ bool parseAndExecute(char *input)
|
||||
|
||||
新命令由以下模块实现。
|
||||
|
||||
## <strong>inventory.h</strong>
|
||||
## **inventory.h**
|
||||
|
||||
```c
|
||||
extern void executeGet(const char *noun);
|
||||
@@ -101,7 +101,7 @@ extern void executeGive(const char *noun);
|
||||
extern void executeInventory(void);
|
||||
```
|
||||
|
||||
## <strong>inventory.c</strong>
|
||||
## **inventory.c**
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -160,7 +160,7 @@ void executeInventory(void)
|
||||
}
|
||||
```
|
||||
|
||||
注意:由于动词名词比较好弄,命令 <em>ask</em> 和 <em>give</em> 只有一个参数:item。
|
||||
注意:由于动词名词比较好弄,命令 *ask* 和 *give* 只有一个参数:item。
|
||||
|
||||
::: warning 🤔 思考题:
|
||||
为什么我们要这样设计?
|
||||
@@ -168,7 +168,7 @@ void executeInventory(void)
|
||||
你能否为这些命令多加几个参数?
|
||||
:::
|
||||
|
||||
从本质上讲,<em>get</em>, <em>drop</em>, <em>give</em> and <em>ask 这些命令</em>除了将项目从一个地方移动到另一个地方之外,什么都不做。单个函数 <em>move 对象</em>可以对所有四个命令执行该操作。
|
||||
从本质上讲,*get*, *drop*, *give* and *ask 这些命令*除了将项目从一个地方移动到另一个地方之外,什么都不做。单个函数 *move 对象*可以对所有四个命令执行该操作。
|
||||
|
||||
## move.h
|
||||
|
||||
@@ -229,7 +229,7 @@ void moveObject(OBJECT *obj, OBJECT *to)
|
||||
::: warning 🤔 思考题:识别一些我们拿不了的物品需要考虑什么因素?
|
||||
:::
|
||||
|
||||
命令“get”使用函数<em>getVisible</em>将名词转换为 object,就像命令“go”一样;请参阅上一章。但是对于对玩家(或其他一些参与者)已经持有的对象进行<em>drop</em>, <em>ask</em>, <em>give 等</em>命令时,我们需要稍微不同的东西。我们将在 <em>noun.c</em> 中添加一个函数 <em>getPossession</em>。
|
||||
命令“get”使用函数*getVisible*将名词转换为 object,就像命令“go”一样;请参阅上一章。但是对于对玩家(或其他一些参与者)已经持有的对象进行*drop*, *ask*, *give 等*命令时,我们需要稍微不同的东西。我们将在 *noun.c* 中添加一个函数 *getPossession*。
|
||||
|
||||
## noun.h
|
||||
|
||||
@@ -318,9 +318,9 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
注意:新函数(45-75 行) <em>getPossession</em> 是 <em>getObject</em> 的装饰器(wrapper)(参见第 52 行),它要么返回匹配的对象,要么返回 NULL(如果没有合适的对象与名词匹配)。
|
||||
注意:新函数(45-75 行) *getPossession* 是 *getObject* 的装饰器(wrapper)(参见第 52 行),它要么返回匹配的对象,要么返回 NULL(如果没有合适的对象与名词匹配)。
|
||||
|
||||
函数 <em>actor 这里</em>用于命令 <em>give</em> 和 <em>ask</em>,但它也可能由其他命令调用。所以我们在<em>misc.c</em>中定义了它。
|
||||
函数 *actor 这里*用于命令 *give* 和 *ask*,但它也可能由其他命令调用。所以我们在*misc.c*中定义了它。
|
||||
|
||||
## misc.h
|
||||
|
||||
@@ -370,7 +370,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
::: warning 🤔 思考题:上面第四行中的函数 actorHere 返回的指针指向什么?
|
||||
:::
|
||||
|
||||
在第 9 行中,有一个详尽的,硬编码的非玩家角色列表(到目前为止,只有一个:<em>守卫</em>)。
|
||||
在第 9 行中,有一个详尽的,硬编码的非玩家角色列表(到目前为止,只有一个:*守卫*)。
|
||||
|
||||
在第 10 章中,我们将开始使用属性作为区分角色与项目和其他非参与者对象的更优雅方式。
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# 6.绘制地图
|
||||
|
||||
作为一个 RPG 游戏怎么能没有地图呢,<em>是时候绘制地图了!</em>
|
||||
作为一个 RPG 游戏怎么能没有地图呢,*是时候绘制地图了!*
|
||||
|
||||
绘制地图的最佳工具始终是:一支铅笔和一张纸。基本地图由<strong>位置</strong>(矩形)组成,由道路(箭头)连接。我们已经在第 3 章中创建了位置,现在我们将开始添加道路。
|
||||
绘制地图的最佳工具始终是:一支铅笔和一张纸。基本地图由**位置**(矩形)组成,由道路(箭头)连接。我们已经在第 3 章中创建了位置,现在我们将开始添加道路。
|
||||
|
||||
在虚拟世界中,“道路”可能是连接两个位置的任何东西:一条路,一扇门,沙漠中。基本上,一段经文具有以下属性:
|
||||
|
||||
- 起点(位置)。
|
||||
- 目标(位置)。
|
||||
- 叙述性描述,例如“森林小径”。
|
||||
- 在 <em>go</em> 命令中往哪里走的描述性标记
|
||||
- 在 *go* 命令中往哪里走的描述性标记
|
||||
|
||||
考虑到这些属性,第 4 章中定义的结构对象就非常适合存储道路了。事实上,一个道路与一个项目或 NPC 并没有太大的不同,它作为“可见出口”存在于某个位置(该位置是起点)。它只是与某些命令的行为不同,特别是命令“go”:应用于道路,<em>go</em>将改变玩家的位置。
|
||||
考虑到这些属性,第 4 章中定义的结构对象就非常适合存储道路了。事实上,一个道路与一个项目或 NPC 并没有太大的不同,它作为“可见出口”存在于某个位置(该位置是起点)。它只是与某些命令的行为不同,特别是命令“go”:应用于道路,*go*将改变玩家的位置。
|
||||
|
||||
```c
|
||||
struct object {
|
||||
@@ -24,9 +24,9 @@ struct object {
|
||||
|
||||
注意:
|
||||
|
||||
- 显然,<em>目的地</em>在大多数其他对象(物品,NPC)中都没有使用
|
||||
- 显然,*目的地*在大多数其他对象(物品,NPC)中都没有使用
|
||||
- 通道总是朝一个方向运行;要双向连接两个位置,我们总是必须创建两个单独的通道。乍一看,这似乎很笨拙,但它确实给了我们很大的灵活性来完善命令“go”的行为
|
||||
- 在大地图上,你可能会发现手动创建所有通道很乏味。所以,我强烈建议你使用自定义工具<em>生成</em>地图中重复性更强的部分。这里不会介绍这一点,但您可能会在第 9 章中找到一些灵感,我们将在其中讨论自动胜场。
|
||||
- 在大地图上,你可能会发现手动创建所有通道很乏味。所以,我强烈建议你使用自定义工具*生成*地图中重复性更强的部分。这里不会介绍这一点,但您可能会在第 9 章中找到一些灵感,我们将在其中讨论自动胜场。
|
||||
|
||||
::: warning 🤔 思考题:为什么创建两个通道可以使我们的程序更加灵活?
|
||||
:::
|
||||
@@ -75,7 +75,7 @@ OBJECT objs[] = {
|
||||
};
|
||||
```
|
||||
|
||||
我们将在 <em>misc.c</em> 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。
|
||||
我们将在 *misc.c* 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。
|
||||
|
||||
## misc.h
|
||||
|
||||
@@ -139,7 +139,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
}
|
||||
```
|
||||
|
||||
我们将在命令“go”的实现中使用新功能<em>getPassage</em>来确定是否存在可以将玩家带到所需位置的通道。
|
||||
我们将在命令“go”的实现中使用新功能*getPassage*来确定是否存在可以将玩家带到所需位置的通道。
|
||||
|
||||
## location.h
|
||||
|
||||
@@ -201,7 +201,7 @@ void executeGo(const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
我们还将使用新功能<em>getPassage</em>来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。
|
||||
我们还将使用新功能*getPassage*来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。
|
||||
|
||||
## noun.h
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
# 7.增大距离
|
||||
|
||||
<em>一个典型的冒险包含许多谜题。</em><em>众所周知,[Infocom](https://en.wikipedia.org/wiki/Infocom)</em><em>的冒险很难完成。解决每个难题可能需要数周甚至数月的反复试验。</em>
|
||||
*一个典型的冒险包含许多谜题。众所周知,[Infocom](https://en.wikipedia.org/wiki/Infocom)的冒险很难完成。解决每个难题可能需要数周甚至数月的反复试验。*
|
||||
|
||||
<em>当玩家操纵角色失败后,如果只是返回“你</em><em>不能这么操作</em><em>”来回应玩家</em><em>,会很 nt,很没意思</em><em>。</em>
|
||||
*当玩家操纵角色失败后,如果只是返回“你不能这么操作”来回应玩家,会很 nt,很没意思。*
|
||||
|
||||
<em>它忽略了</em><em>电脑</em><em>游戏的一个重要方面,而这也是生活本身的一部分:玩家必须从错误中吸取教训。</em>
|
||||
*它忽略了电脑游戏的一个重要方面,而这也是生活本身的一部分:玩家必须从错误中吸取教训。*
|
||||
|
||||
当你的游戏反复输入东西都是,你不能这样做的时候,会显得很无聊的。
|
||||
|
||||
<em>冒险游戏至少应该做的是解释为什么玩家的命令无法完成:“你不能这样做,因为......”这有助于使虚拟世界更具说服力,故事更可信,游戏更有趣。</em>
|
||||
*冒险游戏至少应该做的是解释为什么玩家的命令无法完成:“你不能这样做,因为......”这有助于使虚拟世界更具说服力,故事更可信,游戏更有趣。*
|
||||
|
||||
我们已经付出了相当大的努力让游戏解释<strong>为什么</strong>某些命令是无效的。只需看看<em>名词.c,inventory.c,location.c</em>,<em>move.c</em>中的许多<em>printf</em>调用。但随着游戏变得越来越复杂,这正成为一个相当大的负担。我们需要一种更结构化的方法来检测和处理错误情况。这就是我们在本章中将要讨论的内容。
|
||||
我们已经付出了相当大的努力让游戏解释**为什么**某些命令是无效的。只需看看*名词.c,inventory.c,location.c*,*move.c*中的许多*printf*调用。但随着游戏变得越来越复杂,这正成为一个相当大的负担。我们需要一种更结构化的方法来检测和处理错误情况。这就是我们在本章中将要讨论的内容。
|
||||
|
||||
大多数命令对一个或多个对象进行操作,例如:
|
||||
|
||||
- 玩家拿起一件物品,然后把它交给另一个 NPC。
|
||||
- 玩家沿着一条通道到另一个位置。
|
||||
|
||||
首先要检查的(在[解析器](http://en.wikipedia.org/wiki/Parsing)捕获检测是否会有明显[拼写错误](http://en.wikipedia.org/wiki/Typographical_error)之后)是这些对象<strong>是否存在</strong>;
|
||||
首先要检查的(在[解析器](http://en.wikipedia.org/wiki/Parsing)捕获检测是否会有明显[拼写错误](http://en.wikipedia.org/wiki/Typographical_error)之后)是这些对象**是否存在**;
|
||||
|
||||
失败应该导致类似“这里没有...“或”你看不到任何东西...”等文字出现。在本章中,我们将构建一个通用函数,每个命令都可以使用它来找出玩家是否在可及的范围内。
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
| distHereContained | 一个物体(NPC 或“容器”)存在于玩家的位置,正在拿着另一个物体 | object->location != NULL &&<br/>object->location->location == player->location |
|
||||
| distOverthere | 对象是附近的位置 | getPassage(player->location, object) != NULL |
|
||||
|
||||
第一种情况(对象是玩家)可能看起来微不足道,但它仍然很重要。例如,命令“examine yourself”<em>不应该</em>返回“这里没有你自己”。
|
||||
第一种情况(对象是玩家)可能看起来微不足道,但它仍然很重要。例如,命令“examine yourself”*不应该*返回“这里没有你自己”。
|
||||
|
||||
我试图遵循一个逻辑顺序:附近的事物最高优先级,随后优先级会变低。
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
请注意,我们有七种不同的“这里”案例,但只有一种是“不在这里”。这是因为通常,游戏只需要提供有关玩家可以感知的事物的信息。如果它不在这里,那么就没什么可说的了。
|
||||
|
||||
在最左边的列中,我们为每个案例提出了一个符号名称。我们将在名为 <strong>DISTANCE</strong> 的[枚举](http://en.wikipedia.org/wiki/Enumerated_type)中收集这些名称。
|
||||
在最左边的列中,我们为每个案例提出了一个符号名称。我们将在名为 **DISTANCE** 的[枚举](http://en.wikipedia.org/wiki/Enumerated_type)中收集这些名称。
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
@@ -91,7 +91,7 @@ DISTANCE getDistance(OBJECT *from, OBJECT *to)
|
||||
注:自行实验即可
|
||||
:::
|
||||
|
||||
就这样!我们可以调用此函数并对其返回值进行比较。例如,我们在 noun<em>.c</em>中有以下代码:
|
||||
就这样!我们可以调用此函数并对其返回值进行比较。例如,我们在 noun*.c*中有以下代码:
|
||||
|
||||
```c
|
||||
else if (!(obj == player ||
|
||||
@@ -125,9 +125,9 @@ else if (getDistance(player, obj) >= distNotHere)
|
||||
::: warning 🤔 尝试理解一下这样做的意义
|
||||
:::
|
||||
|
||||
这只是一个例子,让你对这个概念有所了解;您将在下面找到<em>noun.c</em>的实际实现,看起来略有不同。
|
||||
这只是一个例子,让你对这个概念有所了解;您将在下面找到*noun.c*的实际实现,看起来略有不同。
|
||||
|
||||
是时候把事情落实到位了。枚举 <em>DISTANCE</em> 和函数 <em>getDistance</em> 的定义被添加到 <em>misc.h</em> 和 <em>misc.c</em> 中,因为我们将在多个模块中使用它们。
|
||||
是时候把事情落实到位了。枚举 *DISTANCE* 和函数 *getDistance* 的定义被添加到 *misc.h* 和 *misc.c* 中,因为我们将在多个模块中使用它们。
|
||||
|
||||
## misc.h
|
||||
|
||||
@@ -237,7 +237,7 @@ extern void executeLook(const char *noun);
|
||||
extern void executeGo(const char *noun);
|
||||
```
|
||||
|
||||
在函数 <em>executeGo</em> 中,我们可以用检查距离来替换大多数 <em>if</em> 条件。
|
||||
在函数 *executeGo* 中,我们可以用检查距离来替换大多数 *if* 条件。
|
||||
|
||||
## location.c
|
||||
|
||||
@@ -297,9 +297,9 @@ void executeGo(const char *noun)
|
||||
::: warning 🤔 思考题:你能否为 switch 函数增加更多 case 来完善判断条件?
|
||||
:::
|
||||
|
||||
函数 <em>executeGet</em> 也是如此。
|
||||
函数 *executeGet* 也是如此。
|
||||
|
||||
## <strong>inventory.h</strong>
|
||||
## **inventory.h**
|
||||
|
||||
```c
|
||||
extern void executeGet(const char *noun);
|
||||
@@ -309,7 +309,7 @@ extern void executeGive(const char *noun);
|
||||
extern void executeInventory(void);
|
||||
```
|
||||
|
||||
## <strong>inventory.c</strong>
|
||||
## **inventory.c**
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -372,7 +372,7 @@ void executeInventory(void)
|
||||
}
|
||||
```
|
||||
|
||||
最后,我们将调整 noun<em>.c</em>中的约束。我们正在向函数<em>getObject</em>添加两个参数,以便找到特定名词的匹配项,同时忽略任何被认为不存在的对象。这将在下一章中得到真正的回报,我们将在下一章中介绍具有相同标签的不同对象。
|
||||
最后,我们将调整 noun*.c*中的约束。我们正在向函数*getObject*添加两个参数,以便找到特定名词的匹配项,同时忽略任何被认为不存在的对象。这将在下一章中得到真正的回报,我们将在下一章中介绍具有相同标签的不同对象。
|
||||
|
||||
## noun.h
|
||||
|
||||
@@ -463,7 +463,7 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
::: warning 🤔 思考题:你能理解什么时候加 const,什么时候不用吗?
|
||||
:::
|
||||
|
||||
在本章中,<em>距离</em>的概念主要用于在游戏可以给用户的不同响应之间进行选择。但是,距离的好处并不局限于<strong>输出</strong>端;它可以同样很好地用于在<strong>输入</strong>端进行改进。在下一章中,我们将使用距离来提高对用户输入的名词的识别。
|
||||
在本章中,*距离*的概念主要用于在游戏可以给用户的不同响应之间进行选择。但是,距离的好处并不局限于**输出**端;它可以同样很好地用于在**输入**端进行改进。在下一章中,我们将使用距离来提高对用户输入的名词的识别。
|
||||
|
||||
输出样例
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# 8.移动方向
|
||||
|
||||
<em>传统的文本冒险使用</em><em>[指南针方向](https://en.wikipedia.org/wiki/Cardinal_direction)</em><em>进行导航。</em>
|
||||
*传统的文本冒险使用[指南针方向](https://en.wikipedia.org/wiki/Cardinal_direction)进行导航。*
|
||||
|
||||
例如,我们在第 6 章中绘制的地图上,玩家可能想<strong>向东</strong>移动,从田野移动到洞穴。我们可以通过给连接<strong>Cave</strong>的通道标上“east”来实现这一点。但是,我们首先需要解决两个问题。
|
||||
例如,我们在第 6 章中绘制的地图上,玩家可能想**向东**移动,从田野移动到洞穴。我们可以通过给连接**Cave**的通道标上“east”来实现这一点。但是,我们首先需要解决两个问题。
|
||||
|
||||
1. 我们可能仍然想把这段通道称为“entrance”和“east”。但现在,一个对象只能有一个标签。
|
||||
2. 在更大的地图上,具有更多的位置和道路,标签“east”将被定义多次。到目前为止,标签在我们的游戏中是全球独一无二的,没有重复项。
|
||||
@@ -80,9 +80,9 @@ OBJECT objs[] = {
|
||||
};
|
||||
```
|
||||
|
||||
当然,要让这个改动生效,我们还需要调整<em>noun.c</em>中的<em>objectHasTag</em>函数。
|
||||
当然,要让这个改动生效,我们还需要调整*noun.c*中的*objectHasTag*函数。
|
||||
|
||||
<em>同时,我们将让函数 getVisible</em>和<em>getPossession</em> 告知玩家他必须更具体的选择你到底是银币还是金币,而不是随机选择任何一个对象。
|
||||
*同时,我们将让函数 getVisible*和*getPossession* 告知玩家他必须更具体的选择你到底是银币还是金币,而不是随机选择任何一个对象。
|
||||
|
||||
## noun.h
|
||||
|
||||
@@ -91,7 +91,7 @@ extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
|
||||
```
|
||||
|
||||
## <strong>noun.c</strong>
|
||||
## **noun.c**
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -188,7 +188,7 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
问题 #3 可以通过从函数<em>parseAndExecute</em>中删除一个 [空格](http://en.wikipedia.org/wiki/Space_(punctuation))字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是打咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。
|
||||
问题 #3 可以通过从函数*parseAndExecute*中删除一个 [空格](http://en.wikipedia.org/wiki/Space_(punctuation))字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是打咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。
|
||||
|
||||
## parsexec.c
|
||||
|
||||
@@ -246,9 +246,9 @@ bool parseAndExecute(char *input)
|
||||
}
|
||||
```
|
||||
|
||||
模块<em>main.c</em>、<em>inventory.c</em>、<em>location.c</em>、<em>move.c</em> 和<em>misc.c</em>保持不变
|
||||
模块*main.c*、*inventory.c*、*location.c*、*move.c* 和*misc.c*保持不变
|
||||
|
||||
现在对象数组 ( <em>object.c</em> ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。
|
||||
现在对象数组 ( *object.c* ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。
|
||||
|
||||
::: warning 🤔 猜猜看该怎么办?
|
||||
:::
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
## 前言
|
||||
|
||||
本来打算让各位做下面的任务来进行进一步的学习的,但是想了想,实在是,<strong>太无聊啦</strong>!
|
||||
本来打算让各位做下面的任务来进行进一步的学习的,但是想了想,实在是,**太无聊啦**!
|
||||
|
||||
又想让你们来做一个管理系统,但是又想到你们可能会进行无数个管理系统,<strong>这也太无聊啦</strong>!
|
||||
又想让你们来做一个管理系统,但是又想到你们可能会进行无数个管理系统,**这也太无聊啦**!
|
||||
|
||||
因此呢,我打算带大家玩一个文字冒险游戏,如果你想自己体验全流程的话可以试试玩哦!
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
在这种情况下,系统能跑起来才是王道,跑不起来什么都是浮云,追求面面俱到只会增加代码维护的难度。
|
||||
|
||||
唯一可以把你从 bug 的混沌中拯救出来的就是 KISS 法则,它的宗旨是<strong>从易到难,逐步推进</strong>, 一次只做一件事,少做无关的事。
|
||||
唯一可以把你从 bug 的混沌中拯救出来的就是 KISS 法则,它的宗旨是**从易到难,逐步推进**, 一次只做一件事,少做无关的事。
|
||||
|
||||
如果你不知道这是什么意思,我们以可能发生的 `str` 成员缓冲区溢出问题来作为例子。KISS 法则告诉你,你应该使用 `assert(0)`, 就算不"得体"地处理上述问题,仍然不会影响表达式求值的核心功能的正确性。
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
[GDB 快速入门教程](https://www.bilibili.com/video/BV1EK411g7Li/)
|
||||
|
||||
### <strong>GDB 使用表</strong>
|
||||
### **GDB 使用表**
|
||||
|
||||
`run (r)`运行程序
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
`ptype`查看详细信息
|
||||
|
||||
#### <strong>TUI</strong>
|
||||
#### **TUI**
|
||||
|
||||
`ctrl + x + a`开启
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ C 艹可能没那么容易了····比如说虚函数调用,那你就不太
|
||||
|
||||
你可以写任何一个指令,他完全不会检查 也不会优化 编译器默认你知道你在干什么。
|
||||
|
||||
然后 C 编译器就会将这部分代码 <strong>原封不动地 </strong>拷贝进你的二进制代码当中
|
||||
然后 C 编译器就会将这部分代码 **原封不动地**拷贝进你的二进制代码当中
|
||||
|
||||
当然,你可以通过 RTFM 来将 C 语言的变量塞到汇编中处理。
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
引自 nju-ics-pa
|
||||
|
||||
## <strong>光玉</strong>
|
||||
## 光玉
|
||||
|
||||
想象一下你正在玩 Flappy Bird,你今晚的目标是拿到 100 分,不然就不睡觉。经过千辛万苦,你拿到了 99 分,就要看到成功的曙光的时候,你竟然失手了!你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?为什么不让我存档?”
|
||||
|
||||
[**Play Happy Bird**](https://flappybird.io/)
|
||||
[Play Happy Bird](https://flappybird.io/)
|
||||
|
||||
想象一下你正在写代码,你今晚的目标是实现某一个新功能,不然就不睡觉。经过千辛万苦,你终于把代码写好了,保存并编译运行,你看到调试信息一行一行地在终端上输出。就要看到成功的曙光的时候,竟然发生了错误!你仔细思考,发现你之前的构思有着致命的错误,但之前正确运行的代码已经永远离你而去了。你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?”你绝望地倒在屏幕前 ... ... 这时,你发现身边渐渐出现无数的光玉,把你包围起来,耀眼的光芒令你无法睁开眼睛 ... ... 等到你回过神来,你发现屏幕上正是那份之前正确运行的代码!但在你的记忆中,你确实经历过那悲痛欲绝的时刻 ... ... 这一切真是不可思议啊 ... ...
|
||||
|
||||
## <strong>人生如戏</strong><strong>,</strong><strong>戏如人生</strong>
|
||||
## 人生如戏,戏如人生
|
||||
|
||||
人生就像不能重玩的 Flappy Bird,但软件工程领域却并非如此,而那不可思议的光玉就是“版本控制系统”。版本控制系统给你的开发流程提供了比朋也收集的更强大的光玉,能够让你在过去和未来中随意穿梭,避免上文中的悲剧降临你的身上。
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
在本节,我们使用 `git` 进行版本控制。下面简单介绍如何使用 `git` :
|
||||
|
||||
### <strong>游戏设置</strong>
|
||||
### 游戏设置
|
||||
|
||||
首先你得安装 `git` :
|
||||
|
||||
@@ -52,7 +52,7 @@ git init
|
||||
|
||||
进行初始化。初始化后会创建一个隐藏的文件夹名为 `.git` git 会基于这个文件夹来进行版本控制功能。
|
||||
|
||||
### <strong>查看</strong><strong>commit</strong><strong>信息</strong>
|
||||
### 查看 commit 信息
|
||||
|
||||
使用
|
||||
|
||||
@@ -147,7 +147,7 @@ subject为commit概述
|
||||
|
||||
你可以使用 `git log` 查看存档记录,你应该能看到刚才编辑的注释。
|
||||
|
||||
### <strong>读档</strong><strong>(回溯到某一个 commit)</strong>
|
||||
### 读档(回溯到某一个 commit)
|
||||
|
||||
如果你遇到了上文提到的让你悲痛欲绝的情况,现在你可以使用光玉来救你一命了。首先使用 `git log` 来查看已有的存档,并决定你需要回到哪个过去。每一份存档都有一个 `hash code`,例如 `b87c512d10348fd8f1e32ddea8ec95f87215aaa5` , 你需要通过 `hash code` 来告诉 `git` 你希望读哪一个档。使用以下命令进行读档:
|
||||
|
||||
@@ -194,7 +194,7 @@ git checkout -B 分支名
|
||||
|
||||
不同的分支之间不会相互干扰,这也给项目的分布式开发带来了便利。有了分支功能,你就可以像第三视点那样在一个世界的不同时间 ( 一个分支的多个存档 ),或者是多个平行世界(多个分支)之间来回穿梭了。
|
||||
|
||||
### <strong>更多功能</strong>
|
||||
### 更多功能
|
||||
|
||||
以上介绍的是 `git` 的一些基本功能,`git` 还提供很多强大的功能,例如使用 `git diff` 比较同一个文件在不同版本中的区别,使用 `git bisect` 进行二分搜索来寻找一个 bug 在哪次提交中被引入...
|
||||
|
||||
@@ -246,13 +246,54 @@ git checkout -B 分支名
|
||||
|
||||
最后你访问[GitHub 官网](https://github.com)应该会显示你的 dashboard 管理台界面
|
||||
|
||||
#### 第二步:创建 SSH Key 并获取公钥
|
||||
#### 第二步:选择你拉取仓库的方式
|
||||
|
||||
先在 C:\Users\用户名\.ssh 下找有没有 `id_rsa` 和 `id_rsa.pub` 文件
|
||||
点开 github 某个仓库的绿油油的 `<>Code` 按钮,你会发现有三种 clone 方式。
|
||||
|
||||
分别为
|
||||
|
||||
- https(基本无配置,有图形化界面就能用)
|
||||
- ssh(有公私钥设置,没有图形化界面也能用)
|
||||
- gh-cli(github 出品的 cli 工具)
|
||||
|
||||

|
||||
|
||||
#### 【https 方法配置】账号绑定 github
|
||||
|
||||
在命令行配置好你的 id 和邮箱
|
||||
|
||||
```bash
|
||||
git config --global user.name "Zhang San" # your name
|
||||
git config --global user.email "zhangsan@foo.com" # your email
|
||||
```
|
||||
|
||||
在 GitHub 主页,找到 `New` 或者 `Create repository` 一个绿色的按钮,创建一个新的仓库
|
||||
|
||||

|
||||
|
||||
然后填上这个仓库的大名就可以创建了
|
||||
|
||||

|
||||
|
||||
进入你新创建的仓库
|
||||
|
||||
跟随新创建之后的指引,`…or create a new repository on the command line` 内他描述了如何创建一个文件夹、创建一个 README.md 的文件,然后和 github 仓库绑定。
|
||||
|
||||
push 的时候 github 会弹窗索要你的 github 账号和密码,填上去就能用了。
|
||||
|
||||
#### 【ssh 方法配置】创建 SSH Key 并获取公钥
|
||||
|
||||
先在 `C:\Users\用户名\.ssh` 下找有没有 `id_rsa` 和 `id_rsa.pub` 文件
|
||||
|
||||
:::tip
|
||||
这里 `.ssh` 文件夹是一个隐藏文件夹
|
||||
|
||||
如何显示隐藏文件夹请搜索互联网或看这篇文章 [support microsoft 显示隐藏的文件](https://support.microsoft.com/zh-cn/windows/%E6%98%BE%E7%A4%BA%E9%9A%90%E8%97%8F%E7%9A%84%E6%96%87%E4%BB%B6-0320fe58-0117-fd59-6851-9b7f9840fdb2)
|
||||
:::
|
||||
|
||||
如果有就直接跳过这一步
|
||||
|
||||
如果没有,打开 Shell(Windows 下打开 Git Bash <em>前提是你已经安装好了 git 在桌面右键应该会有 Git bash here 选项 </em>),创建 SSH Key:
|
||||
如果没有,打开 Shell(Windows 下打开 Git Bash *前提是你已经安装好了 git 在桌面右键应该会有 Git bash here 选项*),创建 SSH Key:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电子邮件地址
|
||||
@@ -260,7 +301,7 @@ ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电
|
||||
|
||||
打开 `id_rsa.pub`,复制里面的内容
|
||||
|
||||
#### 第三步:绑定 Github
|
||||
#### 【ssh 方法配置】绑定 Github
|
||||
|
||||
登陆 `GitHub`,点击右上角自己的头像,打开 `settings`
|
||||
|
||||
@@ -272,7 +313,7 @@ ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电
|
||||
|
||||
然后,点 `New SSH Key`,填上任意 Title,在 Key 文本框里粘贴 `id_rsa.pub` 文件的内容即可
|
||||
|
||||
#### 第四步:创建仓库并和本地绑定
|
||||
#### 【ssh 方法配置】创建仓库并和本地绑定
|
||||
|
||||
绑定完 GitHub 然后你可以创建仓库了
|
||||
|
||||
@@ -301,8 +342,6 @@ git remote add origin git@github.com:yourname/gitexample.git
|
||||
git clone git@github.com:yourname/gitexample.git
|
||||
```
|
||||
|
||||
> 以上方法是基于 ssh 方式的,下面方法是基于 HTTPS 方式的
|
||||
|
||||
或者你可以跟随新创建之后的指引,`…or create a new repository on the command line` 内他描述了如何创建一个文件夹、创建一个 README.md 的文件,然后和 github 仓库绑定。
|
||||
|
||||

|
||||
@@ -442,7 +481,7 @@ git commit -m "feat(helloworld): add helloworld file"
|
||||
|
||||
这样会在你的名下多出来一份这个同名仓库,而这个仓库你是拥有所有权限的,你可以 clone 你这个同名仓库,更改代码,提交代码之后
|
||||
|
||||
回到源仓库点击 Pull Request 然后创建 PR `New pull request`
|
||||
回到源仓库点击 Pull Request 然后创建 PR `New pull request`
|
||||
|
||||
最上面会提示你说
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 什么是环境?
|
||||
|
||||
环境是<strong>包的集合</strong>,我们一般用 Anaconda 来配置虚拟环境。
|
||||
环境是**包的集合**,我们一般用 Anaconda 来配置虚拟环境。
|
||||
|
||||
[戳这里安装](https://www.anaconda.com/)
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
|
||||
打开 [Python 官方网站](https://www.python.org/),找到“Download”里的“Latest: Python 3.x.y”。
|
||||
|
||||
<strong>下载完成后,请大家按照下图的示意,务必勾选“Add Python 3.x to PATH”,然后再点击“Install Now”,等待安装完成后关闭安装程序。</strong>
|
||||
**下载完成后,请大家按照下图的示意,务必勾选“Add Python 3.x to PATH”,然后再点击“Install Now”,等待安装完成后关闭安装程序。**
|
||||
|
||||
<strong>注意:windows11 安装好后 命令行输入 python 可能会跳到 Microsoft 应用商店 可在 customize installation(自定义安装)next 勾选 install for all users</strong>
|
||||
**注意:windows11 安装好后 命令行输入 python 可能会跳到 Microsoft 应用商店 可在 customize installation(自定义安装)next 勾选 install for all users**
|
||||
|
||||
<font size=5><strong>GNU/Linux 系统</strong></font>
|
||||
<font size=5>**GNU/Linux 系统**</font>
|
||||
|
||||
在终端输入 `sudo apt install python3` 即可完成 Python3 的全部安装流程
|
||||
|
||||
|
||||
@@ -135,9 +135,9 @@ s
|
||||
|
||||
可变对象的示例包括列表和字典。不可变对象的示例包括元组和函数。
|
||||
|
||||
我们假定已经知道了如何使用 `==` 运算符来检查两个表达式的计算结果是否<strong>相同</strong>。
|
||||
我们假定已经知道了如何使用 `==` 运算符来检查两个表达式的计算结果是否**相同**。
|
||||
|
||||
我们现在引入一个新的比较运算符 `is`,它检查两个表达式的计算结果是否<strong>相同</strong>。
|
||||
我们现在引入一个新的比较运算符 `is`,它检查两个表达式的计算结果是否**相同**。
|
||||
|
||||
```python
|
||||
>>> 2 + 2 == 3 + 1
|
||||
|
||||
@@ -37,7 +37,7 @@ iterator = iter(iterable)
|
||||
# do something
|
||||
```
|
||||
|
||||
- 首先,在可迭代对象上调用内置 `iter` 函数以创建对应的<em>迭代器</em>。
|
||||
- 首先,在可迭代对象上调用内置 `iter` 函数以创建对应的*迭代器*。
|
||||
- 要获取序列中的下一个元素,在此迭代器上调用内置 `next` 函数。
|
||||
|
||||
如果没有下一个元素了,怎么办?
|
||||
@@ -76,7 +76,7 @@ StopIteration
|
||||
|
||||
## 英语练习,对迭代器的类比
|
||||
|
||||
<strong>Analogy</strong>: An iterable is like a book (one can flip through the pages) and an iterator for a book would be a bookmark (saves the position and can locate the next page). Calling `iter` on a book gives you a new bookmark independent of other bookmarks, but calling `iter` on a bookmark gives you the bookmark itself, without changing its position at all. Calling `next` on the bookmark moves it to the next page, but does not change the pages in the book. Calling `next` on the book wouldn't make sense semantically. We can also have multiple bookmarks, all independent of each other.
|
||||
**Analogy**: An iterable is like a book (one can flip through the pages) and an iterator for a book would be a bookmark (saves the position and can locate the next page). Calling `iter` on a book gives you a new bookmark independent of other bookmarks, but calling `iter` on a bookmark gives you the bookmark itself, without changing its position at all. Calling `next` on the bookmark moves it to the next page, but does not change the pages in the book. Calling `next` on the book wouldn't make sense semantically. We can also have multiple bookmarks, all independent of each other.
|
||||
|
||||
## 生成器:懒人迭代器!
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# CS61A Sec1
|
||||
|
||||
<strong>观前须知:</strong>
|
||||
**观前须知:**
|
||||
|
||||
本章节内容基于 Berkeley 大学的教材 [Composing Programs](http://www.composingprograms.com/) by [John DeNero](http://www.denero.org/),并在此基础上做了部分修改,是在[知识共享协议](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh)下所许可的。
|
||||
|
||||
@@ -28,7 +28,7 @@ from urllib.request import urlopen
|
||||
|
||||
这个 Python 代码是一个导入语句,加载了在互联网上访问数据的功能。实际上,它提供了一个叫做 urlopen 的函数,它可以在一个[统一资源定位符(URL)](https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL)上访问内容,可以通过它来访问互联网上的数据。
|
||||
|
||||
<strong>语句和表达式</strong>
|
||||
**语句和表达式**
|
||||
|
||||
Python 代码由语句和表达式组成。大体上,计算机程序由以下指令组成
|
||||
|
||||
@@ -37,7 +37,7 @@ Python 代码由语句和表达式组成。大体上,计算机程序由以下
|
||||
|
||||
语句通常描述行动;当 Python 解释器执行一个语句时,它执行相应的动作。另一方面,表达式通常描述的是计算;当 Python 评估一个表达式时,它计算该表达式的值。在这篇文章下,介绍了几种类型的声明和表达方式。
|
||||
|
||||
<strong>赋值语句</strong>
|
||||
**赋值语句**
|
||||
|
||||
```python
|
||||
shakespeare = urlopen('http://www.composingprograms.com/shakespeare.txt')
|
||||
@@ -47,7 +47,7 @@ shakespeare = urlopen('http://www.composingprograms.com/shakespeare.txt')
|
||||
|
||||
将变量名 `shakespeare` 用 `=` 和后面的表达式的值联系起来。该表达式将 `urlopen` 函数应用于一个 URL,该 URL 包含威廉 - 莎士比亚 37 部戏剧的完整文本,全部保存在一个文本文件中。
|
||||
|
||||
<strong>函数</strong>
|
||||
**函数**
|
||||
|
||||
函数封装了操作数据的逻辑。
|
||||
|
||||
@@ -64,7 +64,7 @@ shakespeare = urlopen('http://www.composingprograms.com/shakespeare.txt')
|
||||
|
||||
你可能不了解 `urlopen` 这个函数背后的逻辑,但这不影响你去调用这个函数,这就是函数封装的好处之一。
|
||||
|
||||
<strong>因此,函数是本章节关注的重点。</strong>
|
||||
**因此,函数是本章节关注的重点。**
|
||||
|
||||
我们来看另一个赋值语句:
|
||||
|
||||
@@ -74,7 +74,7 @@ words = set(shakespeare.read().decode().split())
|
||||
|
||||
这个语句将名字词与莎士比亚戏剧中出现的所有出现过的词(重复出现的词只统计一次)的集合联系起来,其中有 33,721(?) 个词。上述语句包含一个读取、解码和分割的命令链,每个命令都在一个中间计算实体上操作:我们从打开的 URL 中读取数据,然后将数据解码成文本,最后将文本分割成单词。所有这些词都被放在一个集合(Set,Python 中的一种数据类型)中。
|
||||
|
||||
<strong>对象</strong>
|
||||
**对象**
|
||||
|
||||
前文中提到的 Set,不仅仅是数据类型,也是一个对象。对象用一种能同时处理两者复杂性的方式,把数据和操作该数据的逻辑无缝衔接在一起。
|
||||
|
||||
@@ -91,7 +91,7 @@ words = set(shakespeare.read().decode().split())
|
||||
|
||||
这是一个复合表达式,其值是所有长度为 6 的、本身和反向拼写都在原集合中的词组成的集合。其中的 `w[::-1]` 是一种隐式表达,它枚举了 `w` 中的所有字母,但因为 `step = -1` 规定了步长是反方向的。
|
||||
|
||||
<strong>解释器</strong>
|
||||
**解释器**
|
||||
|
||||
计算复合表达式需要一个精确的程序,以可预测的方式解释代码。一个能实现程序和计算符合表达式的程序被称为解释器;没错,其实解释器是程序(可能再写一篇文章来讲讲解释器和编译器的区别?)
|
||||
|
||||
@@ -109,9 +109,9 @@ words = set(shakespeare.read().decode().split())
|
||||
|
||||
每种强大的语言都有三种这样的机制:
|
||||
|
||||
- <strong>原始的表达式和语句</strong>,代表了该语言提供的最简单的构建模块。
|
||||
- <strong>组合的方式</strong>,由较简单的元素建立成复合元素。
|
||||
- <strong>抽象的手段</strong>,通过它,复合元素可以作为单位被命名和操作。
|
||||
- **原始的表达式和语句**,代表了该语言提供的最简单的构建模块。
|
||||
- **组合的方式**,由较简单的元素建立成复合元素。
|
||||
- **抽象的手段**,通过它,复合元素可以作为单位被命名和操作。
|
||||
|
||||
在编程中,我们处理两种元素:函数和数据。(很快就会发现,它们其实并不那么明显)。不那么正式地说,数据是我们想要操作的东西,而函数描述了操作数据的规则。因此,任何强大的编程语言都应该能够描述原始数据和原始函数,以及有一些方法来组合和抽象函数和数据。
|
||||
|
||||
@@ -133,20 +133,20 @@ words = set(shakespeare.read().decode().split())
|
||||
0.9921875
|
||||
```
|
||||
|
||||
这些数学表达式使用<em>中缀</em>符号,其中<em>运算符</em>(例如,+,-,*,或/)出现在<em>操作数</em>(数字)之间。Python 包括许多形成复合表达式的方法。我们不会试图立即列举它们,而是会随着我们的学习引入新的表达形式,以及它们所支持的语言特性。
|
||||
这些数学表达式使用*中缀*符号,其中*运算符*(例如,+,-,*,或/)出现在*操作数*(数字)之间。Python 包括许多形成复合表达式的方法。我们不会试图立即列举它们,而是会随着我们的学习引入新的表达形式,以及它们所支持的语言特性。
|
||||
|
||||
最重要的一种复合表达式是<em>调用表达式</em>,它将一个函数应用于一些参数。回顾一下代数,函数的数学概念是一个从一些自变量到因变量的映射。例如,一个求最大值的函数将其的多个输入映射到当中最大值的一个单一的输出。Python 表达函数应用的方式与传统数学中相同。
|
||||
最重要的一种复合表达式是*调用表达式*,它将一个函数应用于一些参数。回顾一下代数,函数的数学概念是一个从一些自变量到因变量的映射。例如,一个求最大值的函数将其的多个输入映射到当中最大值的一个单一的输出。Python 表达函数应用的方式与传统数学中相同。
|
||||
|
||||
```python
|
||||
>>> max(7.5, 9.5)
|
||||
9.5
|
||||
```
|
||||
|
||||
这个调用表达式有子表达式:<em>操作符</em>是括号前的表达式,它包含了一个用逗号分隔的<em>操作数</em>列表。
|
||||
这个调用表达式有子表达式:*操作符*是括号前的表达式,它包含了一个用逗号分隔的*操作数*列表。
|
||||
|
||||

|
||||
|
||||
运算符指定了一个<em>函数</em>。当这个调用表达式被评估时,我们说对<em>参数</em>`7.5` 和 `9.5`<em>调用</em>函数 `max`,并<em>返回</em>一个 9.5 的<em>返回值</em>。
|
||||
运算符指定了一个*函数*。当这个调用表达式被评估时,我们说对*参数*`7.5` 和 `9.5`*调用*函数 `max`,并*返回*一个 9.5 的*返回值*。
|
||||
|
||||
调用表达式中参数的顺序很重要。例如,函数 `pow` 计算第一个参数的第二个参数次方。
|
||||
|
||||
@@ -166,7 +166,7 @@ words = set(shakespeare.read().decode().split())
|
||||
|
||||
不会产生歧义,因为函数名总是优先于其参数。
|
||||
|
||||
此外,函数符号以一种直接的方式延伸到<em>嵌套表达式</em>,其中的元素本身就是复合表达式。在嵌套的调用表达式中,与复合的中缀表达式不同,嵌套的结构在括号中是完全明确的。
|
||||
此外,函数符号以一种直接的方式延伸到*嵌套表达式*,其中的元素本身就是复合表达式。在嵌套的调用表达式中,与复合的中缀表达式不同,嵌套的结构在括号中是完全明确的。
|
||||
|
||||
```python
|
||||
>>> max(min(1, -2), min(pow(3, 5), -4))
|
||||
@@ -205,7 +205,7 @@ Python 定义了大量的函数,包括上一节中提到的运算符函数,
|
||||
|
||||
### 变量名和环境
|
||||
|
||||
编程语言的一个关键方面是它提供了使用变量名来指代计算对象的手段。如果一个值被赋予了一个变量名,我们就说这个变量名与这个值<em>绑定</em>了。
|
||||
编程语言的一个关键方面是它提供了使用变量名来指代计算对象的手段。如果一个值被赋予了一个变量名,我们就说这个变量名与这个值*绑定*了。
|
||||
|
||||
在 Python 中,我们可以使用赋值语句建立新的绑定,其中包含左边的变量名 `=` 右边的值。
|
||||
|
||||
@@ -225,9 +225,9 @@ Python 定义了大量的函数,包括上一节中提到的运算符函数,
|
||||
1.0002380197528042
|
||||
```
|
||||
|
||||
`=` 符号在 Python(以及许多其他语言)中被称为<em>赋值</em>操作符。赋值是我们最简单的抽象手段,因为它允许我们使用简单的名称来指代复合操作的结果。用这种方式,复杂的程序就是通过一步一步地建立复杂度越来越高的计算对象来构建的。
|
||||
`=` 符号在 Python(以及许多其他语言)中被称为*赋值*操作符。赋值是我们最简单的抽象手段,因为它允许我们使用简单的名称来指代复合操作的结果。用这种方式,复杂的程序就是通过一步一步地建立复杂度越来越高的计算对象来构建的。
|
||||
|
||||
将变量名与值绑定,然后通过变量名检索这些值意味着解释器必须保持某种内存,以跟踪变量名、值和绑定。这样的内存空间被称为<em>环境</em>。
|
||||
将变量名与值绑定,然后通过变量名检索这些值意味着解释器必须保持某种内存,以跟踪变量名、值和绑定。这样的内存空间被称为*环境*。
|
||||
|
||||
变量名也可以被绑定到函数上。例如,变量名 `max` 与我们使用的求最大值的函数绑定。与数字不同的是,函数在呈现为文本时很棘手,所以当被要求描述一个函数时,Python 会打印一个识别描述。
|
||||
|
||||
@@ -256,7 +256,7 @@ Python 定义了大量的函数,包括上一节中提到的运算符函数,
|
||||
2
|
||||
```
|
||||
|
||||
在 Python 中,名称通常被称为<em>变量名</em>或<em>变量</em>,因为它们在执行程序的过程中可能被绑定到不同的值。当一个名称通过赋值被绑定到一个新的值时,它就不再被绑定到任何以前的值。人们甚至可以将内置名称与新值绑定。
|
||||
在 Python 中,名称通常被称为*变量名*或*变量*,因为它们在执行程序的过程中可能被绑定到不同的值。当一个名称通过赋值被绑定到一个新的值时,它就不再被绑定到任何以前的值。人们甚至可以将内置名称与新值绑定。
|
||||
|
||||
```python
|
||||
>>> max = 5
|
||||
@@ -315,7 +315,7 @@ Python 定义了大量的函数,包括上一节中提到的运算符函数,
|
||||
1. 计算运算符和操作数的子表达式,然后
|
||||
2. 将作为运算符子表达式的值的函数应用于作为运算符子表达式的值的参数。
|
||||
|
||||
即使这是个简单的程序也说明了关于一般过程的一些重要观点。第一步决定了为了完成一个调用表达式的计算过程,我们必须首先计算其他表达式。因此,计算过程在本质上是<em>递归的</em>;也就是说,作为其步骤之一,它也包括调用规则本身。
|
||||
即使这是个简单的程序也说明了关于一般过程的一些重要观点。第一步决定了为了完成一个调用表达式的计算过程,我们必须首先计算其他表达式。因此,计算过程在本质上是*递归的*;也就是说,作为其步骤之一,它也包括调用规则本身。
|
||||
|
||||
例如,计算
|
||||
|
||||
@@ -328,7 +328,7 @@ Python 定义了大量的函数,包括上一节中提到的运算符函数,
|
||||
|
||||

|
||||
|
||||
这张插图被称为<em>表达式树</em>。在计算机科学中,树(Tree,一种数据结构,我们将在后续的章节中进行讨论)通常是自上而下生长的。树中每一点的对象被称为节点;在这张插图的情况下,节点是与值配对的表达式。
|
||||
这张插图被称为*表达式树*。在计算机科学中,树(Tree,一种数据结构,我们将在后续的章节中进行讨论)通常是自上而下生长的。树中每一点的对象被称为节点;在这张插图的情况下,节点是与值配对的表达式。
|
||||
|
||||
计算它的根,即顶部的完整表达式,需要首先计算作为其子表达式的分支。叶表达式(即没有分支的节点)代表函数或数字。内部节点有两个部分:我们的计算规则所适用的调用表达式,以及该表达式的结果。从这棵树的计算来看,我们可以想象操作数的值是向上渗滤的,从末端节点开始,然后在越来越高的层级上进行组合。
|
||||
|
||||
@@ -357,7 +357,7 @@ Python 定义了大量的函数,包括上一节中提到的运算符函数,
|
||||
|
||||
这个语句不返回一个值,也不在某些参数上调用一个函数,因为赋值的目的是将一个变量名绑定到一个值上。
|
||||
|
||||
一般来说,赋值语句不是被计算而是<em>被执行</em>;它们不产生一个值,而是做一些改变。每种类型的表达式或语句都有自己的计算或执行过程。
|
||||
一般来说,赋值语句不是被计算而是*被执行*;它们不产生一个值,而是做一些改变。每种类型的表达式或语句都有自己的计算或执行过程。
|
||||
|
||||
### 纯函数和非纯函数
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ cs61a 绝对是一个挑战,但是我们都希望你学习并且成功,所
|
||||
- 再次强调,这门课对于大多数学生来说都不是一门简单的课程,你可能会感到不知所措。给你的小伙伴们发送一个信息,并与班上的其他同学取得联系; 一起完成作业或一起学习准备考试,只要你不违反课程大纲中规定的诚实学习的[课程政策](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/about/#academic-misconduct)。
|
||||
- 当遇到问题时,试着组织语言来解释你遇到困难的地方。
|
||||
|
||||
- 这并不需要一个找到懂得如何解决问题的人 (或者甚至是一个人——这种做法通常被称为**橡皮鸭**,因为你可以把一只橡皮鸭当作你的练习对象) ,因为主要目标是让你弄清楚你自己的想法,弄清楚你的理解和代码到底在哪里卡住了。从那里你可以专注于那一部分,以便更好地理解。
|
||||
- 这并不需要一个找到懂得如何解决问题的人 (或者甚至是一个人——这种做法通常被称为**橡皮鸭**,因为你可以把一只橡皮鸭当作你的练习对象) ,因为主要目标是让你弄清楚你自己的想法,弄清楚你的理解和代码到底在哪里卡住了。这样你可以知道应该专注于哪一部分,以便更好地理解。
|
||||
- 做所有的 hw(或者至少认真地尝试)。我们没有给出很多 hw 问题,但那些我们给你的可能会发现是具有挑战性、耗时且有回报的。
|
||||
- 做所有的 lab。其中大部分的设计更多的是作为一个课程材料的介绍,并可能需要半个小时左右的时间。这是一个熟悉新的知识点的好时机。
|
||||
|
||||
@@ -99,15 +99,21 @@ cs61a 绝对是一个挑战,但是我们都希望你学习并且成功,所
|
||||
|
||||
要使用 Ok 来运行指定函数的 doctests,请运行以下命令
|
||||
|
||||
```python3 ok -q <specified function> #会和你要补充的代码一起给出```
|
||||
```bash
|
||||
python3 ok -q <specified function> #会和你要补充的代码一起给出
|
||||
```
|
||||
|
||||
默认情况下,只有没有通过的测试才会出现。你可以使用-v 选项来显示所有的测试,包括你已经通过的测试
|
||||
|
||||
```python3 ok -v```
|
||||
```bash
|
||||
python3 ok -v
|
||||
```
|
||||
|
||||
有时我们会看到类似这样的 ok 指令
|
||||
|
||||
```python3 ok -q control -u```
|
||||
```bash
|
||||
python3 ok -q control -u
|
||||
```
|
||||
|
||||
在终端中输入后,需要按照要求回答问题,有些时候会做为某些函数测试的前置条件
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 3.X 聊聊设计模式和程序设计
|
||||
|
||||
<em>Author: yyl
|
||||
Last revised 2022/08/07</em>
|
||||
*Author: yyl
|
||||
Last revised 2022/08/07*
|
||||
|
||||
## 前言
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# VMware 的安装与安装 Ubuntu22.04 系统
|
||||
|
||||
::: warning 💡 与 wsl 安装二选一 安装了 wsl 不用 VMware
|
||||
::: warning 💡
|
||||
一般与 wsl 安装二选一,因为都是虚拟系统,安装了 wsl 不用 VMware
|
||||
|
||||
文章撰写于 2022 年,可能其中的一些内容已过时。
|
||||
:::
|
||||
|
||||
首先下载 VMware
|
||||
|
||||
如果是 pro16 版本(key <strong>ZF3R0-FHED2-M80TY-8QYGC-NPKYF</strong>)
|
||||
如果是 pro16 版本(key **ZF3R0-FHED2-M80TY-8QYGC-NPKYF**)
|
||||
|
||||
如果是 pro17 版本(key <strong>JU090-6039P-08409-8J0QH-2YR7F</strong><strong> </strong>)
|
||||
如果是 pro17 版本(key **JU090-6039P-08409-8J0QH-2YR7F**)
|
||||
|
||||
本文写的时候用的版本是 pro16,但目前已经更新到 pro17 所以来更新个 key(如下安装与 16 版本无异)
|
||||
|
||||
@@ -25,9 +28,14 @@
|
||||
|
||||
安装过后点许可证 输上面的 key 激活
|
||||
|
||||
[https://mirror.nju.edu.cn/ubuntu-releases/22.04/ubuntu-22.04.2-desktop-amd64.iso](https://mirror.nju.edu.cn/ubuntu-releases/22.04/ubuntu-22.04.2-desktop-amd64.iso)
|
||||
[https://mirror.nju.edu.cn/ubuntu-releases/22.04](https://mirror.nju.edu.cn/ubuntu-releases/22.04)
|
||||
|
||||
去这里下载 Ubuntu22.04 镜像包 iso 选择 `ubuntu-<version>-desktop-amd64.iso`
|
||||
|
||||
:::tip
|
||||
这里推荐使用多线程下载器下载,比如 [IDM](../2.高效学习/2.2优雅的使用工具.md),如果直接用浏览器下载(线程少)可能会出现下载慢、下载失败的情况。
|
||||
:::
|
||||
|
||||
去这里下载 Ubuntu22.04 镜像包 iso
|
||||
|
||||
下好回到 VMware
|
||||
|
||||
@@ -101,7 +109,7 @@
|
||||
|
||||

|
||||
|
||||
<strong>至此 恭喜安装完成!</strong>
|
||||
**至此 恭喜安装完成!**
|
||||
|
||||
之后就可以在桌面上右键
|
||||
|
||||
@@ -109,4 +117,4 @@
|
||||
|
||||
打开命令行
|
||||
|
||||
<strong>开始你的 Linux 学习吧</strong>
|
||||
**开始你的 Linux 学习吧**
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
::: warning 💡与 VMware 安装二选一 安装了 VMware 不用 wsl
|
||||
:::
|
||||
|
||||
先说<strong>坏处</strong>:
|
||||
先说**坏处**:
|
||||
|
||||
1. 开启 hyperv 的后果是 如果你电脑装模拟器玩手游的话 装了 hyperv 你的模拟器是打不开的(目前只有 `蓝叠国际版HyperV版`(性能很差)支持共存 hyperv 和模拟器)
|
||||
2. WSL 很难装辣 安装过程中会出很多 bug 需要你自行 STFW
|
||||
|
||||
## <strong>官方文档</strong>
|
||||
## **官方文档**
|
||||
|
||||
## [史上最全的 WSL 安装教程](https://blog.csdn.net/wojiuguowei/article/details/122100090)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||

|
||||
|
||||
如果你的 windows 版本为<strong>家庭版</strong> 那么 hyperv 选项是没有的
|
||||
如果你的 windows 版本为**家庭版** 那么 hyperv 选项是没有的
|
||||
|
||||
你需要右键以管理员权限打开以下脚本来强行开启 hyperv
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ Linux 命令行中的命令使用格式都是相同的:
|
||||
man 3 freopen
|
||||
```
|
||||
|
||||
### <strong>统计代码行数</strong>
|
||||
### **统计代码行数**
|
||||
|
||||
第一个例子是统计一个目录中 (包含子目录) 中的代码行数。如果想知道当前目录下究竟有多少行的代码,就可以在命令行中键入如下命令:
|
||||
|
||||
@@ -65,7 +65,7 @@ find . | grep '\.c$\|\.h$' | xargs wc -l
|
||||
|
||||
我们最后的任务是统计这些文件所占用的总行数,此时可以用 `man` 查看 `wc` 命令。`wc` 命令的 `-l` 选项能够计算代码的行数。`xargs` 命令十分特殊,它能够将标准输入转换为参数,传送给第一个参数所指定的程序。所以,代码中的 `xargs wc -l` 就等价于执行 `wc -l aaa.c bbb.c include/ccc.h ...`, 最终完成代码行数统计。
|
||||
|
||||
### <strong>统计磁盘使用情况</strong>
|
||||
### **统计磁盘使用情况**
|
||||
|
||||
以下命令统计 `/usr/share` 目录下各个目录所占用的磁盘空间:
|
||||
|
||||
@@ -83,7 +83,7 @@ du -sc /usr/share/* | sort -nr | more
|
||||
|
||||
此时将会看到输出的前几行结果。`more` 工具使用空格翻页,并可以用 `q` 键在中途退出。`less` 工具则更为强大,不仅可以向下翻页,还可以向上翻页,同样使用 `q` 键退出。这里还有一个[关于 less 的小故事](http://en.wikipedia.org/wiki/Less_(Unix)).
|
||||
|
||||
### <strong>在 Linux 下编写 Hello World 程序</strong>
|
||||
### **在 Linux 下编写 Hello World 程序**
|
||||
|
||||
Linux 中用户的主目录是 `/home/用户名称`, 如果你的用户名是 `user`, 你的主目录就是 `/home/user`. 用户的 `home` 目录可以用波浪符号 `~` 替代,例如临时文件目录 `/home/user/Templates` 可以简写为 `~/Templates`. 现在我们就可以进入主目录并编辑文件了。如果 `Templates` 目录不存在,可以通过 `mkdir` 命令创建它:
|
||||
|
||||
@@ -100,7 +100,7 @@ cd Templates
|
||||
|
||||
可以完成目录的切换。注意在输入目录名时,`tab` 键可以提供联想。
|
||||
|
||||
#### <strong> 你感到键入困难吗?</strong>
|
||||
#### ** 你感到键入困难吗?**
|
||||
|
||||
::: warning 💡 你可能会经常要在终端里输入类似于
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ apt-get install vim
|
||||
|
||||
如果您能够遵循上述步骤,并且坚持使用新的编辑器完成您所有的文本编辑任务,那么学习一个复杂的代码编辑器的过程一般是这样的:头两个小时,您会学习到编辑器的基本操作,例如打开和编辑文件、保存与退出、浏览缓冲区。当学习时间累计达到 20 个小时之后,您使用新编辑器的效率应该已经和使用老编辑器一样快。在此之后,其益处开始显现:有了足够的知识和肌肉记忆后,使用新编辑器将大大节省你的时间。而现代文本编辑器都是些复杂且强大的工具,永远有新东西可学:学的越多,效率越高。
|
||||
|
||||
## <strong>Vim 的哲学</strong>
|
||||
## **Vim 的哲学**
|
||||
|
||||
在编程的时候,你会把大量时间花在阅读/编辑而不是在写代码上。所以,Vim 是一个_多模态_编辑 器:它对于插入文字和操纵文字有不同的模式。Vim 是可编程的(可以使用 Vimscript 或者像 Python 一样的其他程序语言),Vim 的接口本身也是一个程序语言:键入操作(以及其助记名)是命令,这些命令也是可组合的。Vim 避免了使用鼠标,因为那样太慢了;Vim 甚至避免用 上下左右键因为那样需要太多的手指移动。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user