查看原文
其他

聊聊编程

精算狗 OSC 计算机与网络安全 2022-06-01

信息安全公益宣传,信息安全知识启蒙。

加微信群回复公众号:微信群QQ群16004488

加微信群或QQ群可免费索取:学习教程

教程列表见微信公众号底部菜单


邪恶的编程咒语

自从我看了 Gary Bernhardt 备受推崇的一个视频 Wat(见下方),就惊异于特定编程语言的怪异行为。相较于其他编程语言来说,某些编程语言的行为更出乎意料。例如,有一整本书是针对 Java 的边缘案例和古怪情况。同样,差不多只要 200 美元你就可以阅读 C++ 规范说明了。

https://v.qq.com/txp/iframe/player.html?vid=j1333z6souh&width=500&height=375&auto=0

下面是我最喜欢的、惊奇的、滑稽的并仍然有效的咒语集合。一般来讲,利用这些古怪的行为被视为坏事,因为代码不应该出乎意料。值得庆幸的是,如果你尝试以下大多数蠢事,有很多代码校检工具(linters)已经准备好嘲笑你了。说了这么多,知识就是力量,那就开始吧。

Python 2 中对 True 邪恶的重赋值

>>> True = False

>>> True

False


谢天谢地,这在 Python 3 中会导致 SyntaxError,因为 True、False 和 None 现在是保留字。它仍远没有 C++ 的那个恶作剧那么邪恶,把 #define true false 悄悄写进同事的开发机器的标准头文件中。

Java 和 Python 中的诡异行为实例

对 Java 新手程序员来说,== 的语义往往使人困惑。甚至在微不足道的情境下,这个操作符的前后矛盾也会使情况变得复杂,即使性能效益是值得的。

Integer a = 100;

Integer b = 100;

System.out.print(a == b); // prints true

 

Integer c = 200;

Integer d = 200;

System.out.print(c == d); // prints false


JVM 会对区间 [-128, 127] 内的值使用相同的引用。更奇怪的是,Python 中也有同样的行为。

>>> x = 256

>>> y = 256

>>> x is y

True

 

>>> x = 257

>>> y = 257

>>> x is y

False


目前为止,还没有特别出乎意料的。

>>> x = -5

>>> y = -5

>>> x is y

True

>>> x = -6

>>> y = -6

>>> x is y

False


似乎 python 解释器使用相同例子的下限是……-5。区间 [-5, 256] 内的整数有同样的 ID。不知怎地,这变得更奇怪了。

>>> x = -10

>>> y = -10

>>> x is y

False

>>> x, y = [-10, -10]

>>> x is y

True


似乎使用解构赋值改变了这里的规则。我不确定为什么是这样。事实上,我在 Stack Overflow 上提了一个问题来试着理解它。我的猜测是,一个列表中的重复值指向同一个对象,用以节省内存。

C 中颠倒的下标符号

颠倒的下标符号,会使所有开发者都头疼。

int x[1] = { 0xdeadbeef };

printf("%xn", 0[x]); // prints deadbeef


这行得通的原因是,array[index] 确实只是 *(array + index) 的语法糖。由于加法的交换性,我们可以交换数组和索引,并得到同样的结果。

C 中的“倒数”操作符

–> 操作符第一次被看到时,似乎是句法错误。在你意识到它可编译时,它看起来像未被记载的语言特性。幸运的是,两者都不是。

for (x = 3; x --> 0;) {

    printf("%d ", x); // prints 2 1 0

}


–> “操作符”实际上是两个操作符,在这个背景下解析为 (x–) > 0。众所周知,大量使用会导致困惑,这完全是邪恶的。

C 中的 sizeof 操作符

sizeof 操作符是一个编译时操作符,这给予了它有趣的属性。

int x = 0;

sizeof(x += 1);

if (x == 0) {

    printf("wtf?"); // this will be printed

}


由于 sizeof 操作符的例子是对编译时进行评估的,(x += 1) 不会运行。另一件趣事是,研究表明 printf(“wtf?”) 是最普遍的没有被 push 的代码。

Lua、Smalltalk、MATLAB 及其他语言,索引由 1 开始

/r/programminghumor 一直在用“indexing starts at 1”表情包取乐。令人震惊的是,有大量编程语言使用从 1 开始的数组索引。可以在这里找到更全面的清单。

Ruby 中的 0 被判为 true

… and only Ruby. *

在 Ruby 中是这样。*

if 0 then print 'thanks, ruby' end # prints thanks, ruby


* 修订:Reddit 上有人指出,这在 Lua、Lisp 和 Erlang 中也成立。

C 中的 Trigraph、Digraph 和 Token

由于历史原因,C 语言中的非字母符号有替代品。

if (true and true) { // same as if (true && true)

    printf("thanks, c");

}


有些外国设备,例如 IBM 3270,在 C/C++ 中不提供某些常用符号,所以提供了 digraph、trigraph 和 token 来避免排斥特定字符集。

读懂编程

编程是什么?


简单的说,“编程就是指导计算机执行任务的行为”。编程通常被认为是编码。


那么,什么是计算机程序?计算机程序就是计算机所执行的一系列的指令。


上面所定义的计算机指的是任何能够执行代码的设备。可能是智能手机、ATM机、黑莓PI、服务器等等。


一个关于编程的好的类比


首先,我们的日常生活是有规律可寻的。宇宙的运行某种程度上来说是可预测的;例如白天黑夜、季节变换、日出日落。人类在早上起床然后去上学或者上班的循环里周而复始。工作上我们从别人那里获得指令,比如上级。再比如烹制一道菜可以分解成有限的几个步骤。


再次,比如当我们使用智能手机的时候,有部分代码运行的在后台不可见的。再比如在电脑上将鼠标从一个地方移动到另一个地方,看起来很简单,但实际上,这需要运行很多行代码。又如在Google Docs上输入文字这样一个看似简单的操作,在后台也需要运行相当多的代码。代码随处可见。


计算机程序也可以称为代码。不要使用"codes"这个词来称呼代码(因为“code”只能用单数形式)。


计算机的自然语言


就像人类一样,机器也有属于他们的自然语言。但计算机无法理解人类的语言。计算机的自然语言实际上是二进制代码(binary code)——1和0。它们代表了两种状态:有(1)和无(0)


它就是电子设备的自然语言。但是要让人类用这种二进制代码来交流的话,我们一定会抓狂的!


进入程序设计语言


为了和这些只讲二进制语言的机器交流,我们需要一种与我们的自然语言(比如英语、法语、阿拉伯语或斯瓦希里语。)比较接近的语言。程序设计语言就是一种和人类语言相近的语言。但是它们跟加结构化而且必须完全学会才能够使用它们。


程序设计语言可能是高级程序设计语言或是低级程序设计语言。高级语言与机器语言的差别要比低级语言更大。这种“大的差别”通常被称之为抽象(abstraction),这一点我们在这个系列中不会深入讨论。这样我们就不会分心(distracted)了:)


计算机需要一种方法来理解我们的语言。为此,我们需要一个翻译器(Translator)。


什么是翻译器


源代码是指以特定编程语言编写的代码。 更多内容在第2部分。


翻译器有责任将您的源代码转换为机器语言。 这也被称为二进制化。 记住1和0。 我们可以将二进制文件称为目标代码,程序或今天的常用词:应用程序。



翻译器可以是以下任何一种:


  • 解释器


  • 编译器


  • 混合解释编译器


  • 汇编器


解释器


一些语言是被解释的。 翻译器逐行处理源代码,程序或应用程序运行每一行代码。 这意味着解释从运行代码开始,直到它遇到错误。 然后解释器停止工作,报告错误。 更多内容在第3部分。


Python是解释型编程语言的一个很好的例子。


编译器


编译器的工作方式跟解释器不一样。编译器会通过编译过程将所有源代码全部转换成为二进制文件。然后执行该二进制文件。如果源代码中存在错误,在编译的时候编译器会检测出来并予以标示。这将会中断编译过程,并且不会生成二进制文件。


解释器是一行行的翻译源代码并执行的,只有当前行的源代码执行完后才能进行下一行代码的翻译。而编译器则是一次性将所有源代码翻译成一个文件(二进制),并执行这一整个文件。


还记得计算机程序的定义吗?就是计算机所执行的一系列的指令。


一个正在执行的程序通常被称为进程。这样的程序需要占用计算机或智能手机一定的资源,如内存、硬盘空间还有文件系统。正在执行的程序也可以说成正在运行


当我们执行一个计算机程序的时候,我们使用“运行(run)”这个词来描述。程序运行所花的时间一般被称为该程序的运行时。


计算机程序通常也被叫做应用程序(Apps)。我们经常会把程序和它们所运行的、或者设计运行的平台或者环境联系起来。比如web应用程序是运行在web浏览器上的,谷歌电子表格(Google Spreadsheet)就是一个例子。还有移动应用程序,是运行在智能手机上的,如CandyCrush。另外还有桌面应用程序,如Evernote的桌面应用。


再说一遍,解释型源代码是直接从源文件上执行的。而编译型源代码则是先转换成二进制文件。之后再执行该文件。编译型源代码编译成功后,在运行时仍有可能发生错误。详见第3部分。


混合解释编译器


混合解释编译器是解释器和编译器的组合。流行的混合编程语言是 Java 。Java 首先将你的源代码编译为称为字节码(Bytecode)的中间格式。


字节码然后由称为虚拟机的运行时引擎解释和执行。这使得混合翻译器能够在各种操作系统上运行字节码。


Assemblers 汇编器


汇编器是将底层的汇编语言翻译成二进制。


对于这个系列,我们只关注高级语言。


分析转译器的一个好方法就是把它们看成一个程序。你需要下载或获取它们,将其安装在计算机系统上并了解其基本工作流程。


常见问题


这里有一个初学者通常都会问的问题。


我应该先学哪门语言?


现在有数百种编程语言。他们按流行度、社区活跃度、长期支持、教育用途和商业用途排名。也可以按技术性来排序,比如是否是函数式的、命令式的、静态的、强类型或弱类型的。


某些语言比其它的更合教学。这些语言以教学为目而不是以商业用途为目的。比如那些给孩子们用于学习编写代码的语言。


也存在非常强大,易于安装和学习的语言。Python 就是其中之一。我一般会建议初学者学习 Python。



如果你有兴趣探索怎么选择“第一语言”,这里有 Philip Guo 的一些研究(https://cacm.acm.org/blogs/blog-cacm/176450-python-is-now-the-most-popular-introductory-teaching-language-at-top-u-s-universities/fulltext)。


在学习一门新语言的时候你就应该知道语言翻译器。这是安装配置在你的计算机系统中的一个程序。


我非常推荐从如何使用 CLI(Command Line Interface,命令行界面) 开始学习。CLI 是一个终端或 Shell。你可以把终端看作 GUI(Graphical User Interface,图形用户界面)。


在 GUI 中,你会通过鼠标与计算机进行交互。同时也会依赖目录以及其它你事情的视觉表现。


但是,如果使用 CLI,你会通过命令与计算机进行交互,你需要在提示信息或闪烁的鼠标后输入命令


$_


在 Windows 下,随机终端就是命令提示符。而对于 Mac 和 Linux 用户,已经存在 Bash 终端。如果想在Windows 下有相同的体验,就安装 Git Bash 或者 PowerShell。


继续前进


你现在已经不知不觉进入到了编程中。你需要做好准备开始写第一行代码了。


你需要从下面这些内容开始:


  • 计算机系统

    这时候你需要的不是复杂或昂贵的计算机,你只需要一台工作正常的就行。


  • 安装 CLI

    我推荐你从这个速成教程(https://learnpythonthehardway.org/book/appendixa.html)开始学习使用 CLI。


  • 安装文本编辑器

    我们会在第二部分回到这个主题


  • 学习一门编程语言

    在这个系列中你会学习一些基础,建立适用于多数编程语言的基础知识。


询问


  1. 开始编程需要哪些基本的工具?


  2. 你将在 Bash(CLI) 下使用什么命令?

  • 检查你的当前目录

  • 进入一个叫 ‘bin’ 的目录(现在 bin 是当前目录)

  • 创建一个新叫 ‘lib’ 的新目录

  • 创建一个叫 ‘book.py’ 的文件

  • 列出当前目录的所有内容


小结


我们已经了解了基本编程知识,也介绍了翻译器。“源码” 对你来说不再陌生。我们将会在下一部分仔细分析源码。


小测验答案


开始编程时你需要的基础工具有哪些?


  • 一台计算机,一个文本编辑器,一个shell(终端),一个编译器/解释器。


在bash(CLI)中你使用什么命令完成以下操作?


  • 查看当前目录: pwd

  • 切换到'bin'目录下 : cd bin

  • 创建一个名为'lib'的新目录: mkdir lib

  • 创建一个名为‘book.py’的新文件: touch book.py

  • 列出当前目录下所有的内容: ls


源代码


现在你已经理解了编程的概念,我们来分析源代码。


源代码基本上就是一个文件,就像 Microsoft (.doc) 文件,但略有不同。它是一个原始的文本文件,使用像 Windows 记事本这样的简单编辑器就可以写出来。回忆前面章节中提到过,你需要解释器或编辑器来把源代码转换为二进制。源代码必须保存在文件中,这样可以作为翻译器的输入。


根据你写程序的语言,会将源代码保存在指定扩展名的文件中。Python 的扩展名是 ‘.py’。Java 的是 ‘.java’。Php 的是 ‘.php’ 还有 PERL 的是 ‘.pl’ 等等。


你在写完源代码之后,必须使用翻译器来运行。比如,这里有一个例子说明如何使用 python 命令来运行 Python 源代码。


入门:你的第一个程序


  • 参考这篇说明文档,然后在你电脑上设置 Python。


  • 安装一个简单的编辑器,用这个编辑器去编写你的源代码。在开始的时候,你可以选择使用 sublime text 这个编辑器。


  • 打开编辑器,新建一个文件,然后写入以下内容:


print 'hello world!'


  • 谨记,将文件保存并命名为main.py。


  • 在你的 CLI 中,切换目录到 main.py 所在的目录,然后输入以下命令:


$ python main.py


  • 在你的 CLI 中会输出以下结果:



源码分析


现在我们会研究这段经典代码的内容。以下是代码常规的几个组成部分:


关键字


通常说的关键字是一些简短而且可以人为阅读的单词。每种语言都有自己的关键字,它们很特殊。我们稍后还会回到这个主题。你需要学习并记住一些关键字。这里有一个 Python 中已知并已使用在使用的关键字。



标识符


有一些是你发明的词 —— 是的,就是你,一个程序员。这些词通常被称为标识符。它是由你或者其它程序员创造的,它们以插件的形式打包,通常被称为库。


Math 库可以用作库的示例。它提供一些像平方根(Math.sqrt)这样的函数供 JavaScript 访问。


许多编程语言都有大量自己的库。这些通常都作为它们的 SDK(Software Development Kit,软件开发包)。你可以随编译器下载这些库,然后用于构建技术、应用和项目。除此之外,有一些框架,它们对构建一个特定的 Web 或移动平台很有帮助。


某些标识符是与你使用的语言绑定的,而且可能不是作为用户命名的标识符来使用。比如 Java 中的 string。这类标识符,与关键字一起被称为保留字。它们不是关键字,但像关键字一样特殊。


所有关键字都是保留字,但并不是所有保留字都是关键字。


你选择的单词对任何第一眼看到它的人来说都有意义。


标识符常用于命名变量,我们将对此进行深入研究。


基本数据类型


你会发现在源码中,数据有各种不同的类型,例如:数字 (3,5.7,-100,3.142)、字符 (M,A)。在一些编程语言中,数字会被更加细致地划分为更为精确的类别,例如整型。


整型可以被分为有符号 (Signed) 和无符号 (Unsigned)、长整性、短整型。长和短取决于系统给数字所保留的内存空间的大小。当然还有一些数字是有小数部分的,在现在你所学的编程语言中,我们通常称这一类数字为双精度浮点型 (double) 和单精度浮点数 (float)。


同时我们也使用布尔值 (boolean) 这个数据类型来表示 真(true) 和 假(false)。


复合数据类型


上面所说的数据类型,都是最基本的几种数据类型。我们可以通过这些基本的数据类型来构建各种类型的复合数据类型。


数组(Array) 就是一种最简单的复合数据类型。字符串 (String) 是一个字符的数组集合。在我们编写代码的时候是无法离开这些复合数据类型的。


一些字符的组合是字符串 (String)。简单说,一个字符串对于计算机来说,就相当于一个词语对于人来说。“Thermometer”这个词由11个字符组成 —— 我们简称为字符串。对于字符串的处理是一个很大的方面,这其中有很多东西需要去学习,并且对于每一个有抱负的程序员来说,都必须要很用心地学。


我们所使用的绝大多数编程语言中都提供了复合数据类型。当然我们也使用其他的一些复核数据类型,比如说类(class)。这些也被称为面向对象编程。


变量


变量可以简单理解为对内存地址的命名。我们有时候会想在代码中保存数据,使之可以反复使用。通常是编译器/解释器来为我们保留这些内存地址。我们需要对这些内存地址命名以便稍后可以访问。看看下面的 Python 代码段:


pet_name = 'Hippo'

print pet_name


pet_name 就是一个变量。因为保存在 pet_name 中的数据是字符串,我们称之为字符串变量。另外有数值变量等。变量按其类型进行分类。


常量


常量是在整个程序生命周期内都不会改变的值。我们使用大写字母来强调某个值是常量值。某些语言提供了创建常量的方法,另外一些则没有。


某些语言提供了声明变量类型的特权。我们通常称之为强类型语言。Java 就是很典型的例子。


另外一些语言没提供这一特性,它们是弱类型动态类型语言。Python 就是其中之一。


下面是在 JavaScript 中声明常量的示例。


const petName = 'Hippo'


字面量


很多源代码中都充斥着需要通过编辑源代码来改变的数据类型,我们称之为字面量,不要将之与变量或常量混淆。字面量可以在浏览代码的时候看到。它们可能是字符串、数值、小数或其它数据类型。


在上面的源代码片段中,‘Hippo’ 就是字面量 —— 一个字符串字面量。它会一直是 ‘Hippo’ 直到你在源代码中把它修改掉。在学习编写代码的过程中,你要学习如何采用一种容易的方法来管理源代码中的字面量,避免对源代码进行大量改动。


标点/符号


从大量源代码中你会发现不同的语言使用着不同的标点符号。与 Python 相比,Java 的标点符号就比较多。


常见的标点符号包括逗号(,)、分号(;)、冒号(:)、花括号({})、圆括号(())、方括号([])、引号("")、管道符号(|)、斜线(\)、点号(。)、问号(?)、插入符号(^) 和百分号(%)。


欢迎来到编写代码的世界,这里标点符号会是你的好朋友。你会发现自己会经常使用它们。


运算符


你写代码去进行运算操作的概率是非常高的。即使从最微小的方面, 你也会在你的代码中进行分配运算。 我们展示了我们所使用的程序语言中大量的操作符。示例中包括加法(+), 除法(/) 乘法(*), 减法(-) 和 大于(>)。


运算符通常分按以下分类:


  • 分配运算符

    它有时候被误解为等于. 等于用来比较两个值。分配运算符给变量赋值, 例如 pet_name = 'Hippo'。


  • 算术运算符

    这些组成的操作符用来执行算术任务,例如加法和减法。一些语言提供了某些其他语言没有的算术操作。例如模运算 (%)返回除法操作的余数。


  • 关系运算符

    用来比较值。它们包括大于,小于,等于,不等于。它们的表示根据你学习的编程语言而变化。 在某些语言中不等于用<>表示, 而其他语言中, 使用 != 或 !==。


  • 逻辑运算符

    用来计算逻辑操作。这些通常使用的逻辑运算符是 and, or, not. 某些语言实用其他符号来表示,比如&& 表示 and, || 表示 or, 和 ! 表示 not。逻辑操作值的运算结果通常是布尔值true或false。


注释


在你的代码活动中,注释是非常重要的组成部分。它用来向其他程序员解释你的代码。这是通过在部分代码中偶尔添加注释完成的。通过注释,你可以引导其他程序员理解你的代码处理什么样的数据和产生什么样的输出。


通常,编译器忽略代码中的注释行。


注释随语言而变。 # 是 Python 的注释符。


这是 Python 中的注释示例。


# program snippet to compute fibonacci of N numbers


在 Java、C 和 C++ 中, 也有像 Python 中 # 的单行注释符, 但它们使用 // 替代。同样有多行注释符 /* … */ 。你可以阅读更多你所选择学习语言的注释。


空格键和 Tab 键


你所写的代码中会有一些空白间隔。这是通过敲空格键或者 Tab 键完成的。


前行


确保在电脑系统上正确地安装 Python ,并运行你的第一个程序。


小测验


这是你的一个小测验。


识别出下面的 Java 源码片段中我们目前所学的不同的元素:


// a recursive implementation of Factorial

import java.util.Scanner;

class RecursiveFactorial

{

 public static void main(String[] args)

 {

  Scanner input=new Scanner(System.in);

  System.out.print("Find the Factorial of: ");

  int num=input.nextInt();

  System.out.println("Factorial of "+num+" = "+fact(num));

 }


static long fact(int n)

 {

  if(n<2) return 1;

  return n*fact(n-1);

 }

}


小结


你已经了解什么是源代码了,并已经体验了典型的源代码的内容布局。


编译或转译,你的代码可能由于一系列原因无法运行。这些原因通常与你的源代码中的错误相关。 这些错误被称为 bugs 。


查找和删除这些 bug 的行为被称为调试(debugging),并且是你作为程序员必须学习的技能。我们将在下一部分中研究 bug 究竟是什么。


小测验的答案


请识别下面Java源代码片段中我们已经学习的不同元素:


关键词:


import, class, public, static, void, new, int, long, if, return


标识符:


java, util, Scanner, RecursiveFactorial, main, String, args, input, System, in, out, print, println, num, nextInt, fact, n


字面量:


字符串常量 — “Factorial of ” = “Find the Factorial of: ”

整型常量 — 2, 1


运算符:


赋值运算符 =

连接符+ (用于字符串的连接)

小于 <

乘法*

减法-


标点符号:


{ } [ ] ( ) ; .


注释


// Factorial的递归实现


Debugging 调试


一旦你开始尝试编写代码片段,或者你正在尝试基于代码解决实际问题,你将很快意识到,你的程序总是会有崩溃、被打断并停止运行的一些时刻。


这通常是由错误引起的,统称为运行时错误或异常。从我们的代码中查找和删除错误的行为被称为调试。通过更好地调试代码,你的程序会变得更好。我们不仅调试我们自己的代码,还可以调试其他程序员编写的代码。


开始之前,我们需要能够识别源代码中可能出现的常见错误。


语法错误


这些错误不会让你的源代码通过编译。它们会在编译期或在解释源码的时候被检测到。这些错误也很容易被 Lint 工具检查到。我们稍后会了解更多关于 Lint 工具的内容。


语法错误多数情况下是由于你在编码时破坏了对应语言的预期形式或结构引起的。比如在某个语句中少写了反括号。


语义错误


语义错误也被称为逻辑错误,它是所有错误中最麻烦的一种。语义错误并不容易检测。语义错误的标志之一是程序可以正确运行但不能产生预期的输出。


看看下面这个例子:


3 + 5 * 6


按优先顺序,即俗称的 BODMAS,在数学中我们知道会先计算乘法,得到的结果会是 33。如果程序员认为先计算的是加法,将会得到与预期不同的结果。像这样的错误就是语义错误,是关于其所表达的意义的错误而不是结构(语法)方面的错误。


给 3 + 5 加上括号之后会得到我们想要的结果。


(3 + 5) * 6


Run-time Errors 运行时错误


与语义错误一样,编译时也不会检测到运行时错误。与语义错误不同的是,运行时错误会中断程序,并阻止其进一步执行。它们通常由源代码中的一些意外计算结果引起。


示例如下:


input = 25

x = 0.8/(Math.sqrt(input) - 5)


上面的代码片段将成功编译,但输入 25 将导致 ZeroDivisionError。这是一个运行时错误。另一个流行的例子是 StackOverflowError 或 IndexOutofBoundError。真正重要的是你需要识别到这些错误并学习如何处理这些错误。


总是存在由于你的源程序在其运行的平台或环境中使用内存和空间而引起的错误。它们也是运行时错误。像 OutOfMemoryError 和 HeapError 这样的错误通常是由于你的源程序耗尽资源造成的。良好的算法知识将有助于你编写更好地使用资源的代码。


重写代码以获得更好的性能的过程称为优化(optimization),另一个不太相关的词是重构(refactoring)。当你花更多的时间编码时,你也应该考虑到这些。


调试


以下是几条关于如何调试你代码的小贴士:


  • 使用 Linters

    Linters 是辅助扫描源代码的工具,用以检查它们是否符合你所编写的语言中的预设的标准。许多编程语言都有对应的 linters。一定要找一个你正在学习的语言的版本。


  • 优选 IDE,而不是简单的编辑器

    你可以选择为你正在学习的语言所设计的 IDE。IDE 代表集成开发环境。它们是用于编写、调试、编译和运行代码的软件。他们通常带有强大的调试工具包,支持查看或单步执行代码。

    Jetbrains 创造了很多伟大的 IDE,例如 Webstorm 和 IntelliJ。IDE 还有 NetBeans, Komodo, Qt editor, Android Studio, XCode (已移植到 Mac 上),这里仅罗列部分。.


  • 大声地朗读你的代码

    当你正在查找语义错误时,这通常很有用。在大声朗读代码的同时,你很可能会找到错误位置。这可能会让你灵光乍现,发现错误所在。


  • 阅读错误日志

    当编译器给出错误时,请确保查看行号或被标记的代码部分。


未来之路


作为初学者,你将从书籍、在线教程或视频中学习如何编写代码。当你看到代码时,你会经常输入它们。


当你正在编写或运行此类代码时,下面是你应该做的事情:学会破坏它们。 你怎么能做到这一点呢?


改动某些内容以查看代码的行为。这样做,以防止你对任何事情做假定,你会非常确定你已了解究竟发生了什么。


小测验


1 . 请问下面 Python 代码片段中可能存在的 bug 是什么?


items = [0,1,2,3,4,5]

print items[8]

//hint: items here is an Array, with 6 items. To retrieve the 4th item for example, you will use items[3]. We start counting from 0.


2. 请问下面 Python 代码片段中可能存在的 bug 是什么?


input = Hippo'

if input == 'Hippo':

  print 'Hello, Hippo'


本节总结


恭喜!你已经对 bug 这个词不再陌生,找出 bug 也不再话下。下一步,你需要看看我们每天编写代码的通用流程。


小测验的答案


请问下面 Python 代码片段中可能存在的 bug 是什么?


(1) 运行时错误:索引越界错误

(2) 语法错误:第一行缺少起始的引号


基本编码流程


代码行,表达式和语句


所有源代码的基本单位是 LOC (Line of Code,代码行)。最简单的程序是一行代码。LOC 可以是一个关键字、一个符号或者一个语句。只要代码位于单独的一行,它就是 LOC。


让我们来看看一个简单的代码行:


area = 0.5 * base * height


0.5 * base * height 是一个表达式。一个表达式是有运算符和运算子组成的。在上面给出的例子中,运算子是 0.5,base 和 height。你可能会回想到 0.5 是一个浮点型常量,而 base 和 height 是变量。运算符是 *(乘法运算符)。


作为 LOC,表达式单独存在可能并没有什么意义。当我们将表达式的值赋值给另一个变量时,比如在上述代码中的 area,我们将其称之为语句。当我们将表达式附加到关键字之后时,它仍然是一个声明,例如:return 0.5 * base * height


在本节的后续部分我们将用符号 S 表示一条语句。在一系列语句或语句集中,第 n 条语句将使用 Sn 表示,


要快速掌握编程,一个很好的起点是理解基本的编码流程。基本流程也被称为控制流。一旦你理解了这些流程,你会发现他们在许多你所学习的编程语言中都存在。


请注意,本文中给出的例子都是非常基础的。你需要参考你正在学习的语言,以深入了解其所提供的关键字。


此外,这里介绍的基本流程与编程中的设计模式是不同的。首先了解这些基本流程。 随着你学到更多的东西,你可以在编程中慢慢涉及到常见的设计模式。


下面是基本的编程流程:


  • Sequential 顺序


  • Conditional/Branching 条件/分支


  • Iteration/Repetition/Loops 迭代/重复/循环


顺序


这是最基本的流程,意指一个语句在另一个语句之后执行。在实际意义上,每一个其他的流程都可以使用一个顺序的流程解决(更多的解释在此后面)。


S1

S2

S3

.

.

.

Sn


在某些编程语言(如JavaScript)中,S3 可能在 S1 之前执行。如果 S1 被某些可能耗时较多的任务阻塞的话,例如数据库或文件操作,我们通常此类任务称为异步任务。有很多类似的情况需要了解。不要太担心,当你开始编程语言学习的时候,这些都是入门要学的知识。


条件/分支


条件/分支是指根据对条件的检测来执行的语句。这里用到的关键字是 if。它是最常用的代码流程之一。


下面是最简单的条件语句:


if (condition) then:

  S1


上面的例子中,可能会执行 S1,也可能什么都不做。S1 仅在给定的条件为真的时候才会执行。


接下来是另一个条件语句的示例:


if (condition) then:

  S1

  S2

else:

  S3

  S4


通过阅读我们知道它会根据条件执行 S1-S2 或 S3-S4。如果条件为真,S1 和 S2 会执行,否则执行的是 S3 的 S4。实际上下面这个是顺序流程:


S1

S2


还有多条件分支的样式:


if (condition1) then:

  S1

else if (condition2) then:

  S2

else:

  S3


这里,如果 condition1 为真,会执行 S1。否则会检测 condition2,如果它为真,执行 S2。依此类推。 


在多条件语句情况下,多数编程语言都提供了 switch 语句。下面是 switch 语句风格的代码:


switch value:

  case condition1:

    S1

    break


case condition2:

    S2

    break


default:

    S3


condition1 和 condition2 都会与 switch 语句中的值进行比较,如果其中一个比较结果为真,则对应 case 语句中的代码块会被执行。


还有其它一些条件流程。某些是你决定学习的语言所特有的,比如条件运算符(: ?),还有其它增强分支的关键字,比如 cycle 和 break。一定要花时间去理解条件/分支流程。


迭代/重复/循环


迭代/重复流程会让语句在某种条件下运行很长时间,直到给定的条件不再为真才停止执行。


下面是这种模式的示例:


while (condition):

  S1

  S2


上面的示例中,语句 S1 和 S2 可能只执行一次,也可能执行很多次,也可能根本不会执行。如果给 while 语句的条件 condition 为真,S1 和 S2 就会执行。while 每次都会检查 condition,只要 condition 为真,S1 和 S2 就会一直执行下去。


直到 condition 变成 false 的时候,对 S1 和 S2 的执行才会停止。


上例如果前三次条件都为真,结果如下:


S1

S2

S1

S2

S1

S2


上面的是什么流程?如果你回答是顺序流,那么你回答正确。正如我们看到的,其它流程会被解析为顺序流。


下面是另一个迭代模式:


do:

  S1

  S2

while (condition)


这个示例中,S1 和 S2 至少会被执行一次。这是因为执行语句是在条件检测之前。


多数编程语言中,像 do 和 while 这样的关键字用于实现重复流程。还有一个常用的关键字是 for。下面是 for 语句的常见形式。


for (initialvalue; condition; decrement/increment initialvalue):

  S1

  S2


很多语言还有 foreach 用于遍历像数组或结构体这类复杂对象的每一个元素。


小测试


识别下列Python代码片段中的编码流:


numlist=[]

cnt=0

while cnt >= 0:

    m=int(raw_input())

    if m < 0:

        break

    numlist.append(m)

    cnt=cnt+1


总结


这里涵盖的流程是基本流程。 有一种方法可以将一堆代码组合在一起,并给它们一个名称。 这样一来,您可以在需要时立即调用这一堆代码。 这被称为程序。 在一堆代码执行一些操作并返回值的情况下,您就有了一个函数。


程序和功能的实现方式因语言不同而存在差异。 你不能跳过这些来获得任何语言的基础知识。 它们对组织您的代码非常重要。 实际上,这是代码开始构建的块也是被称为模块化编程的地方。


还有其他流程,您将在学习函数时学习一个流程,这时想到的就是递归


然而,您会发现在程序和函数中仍然是我们在这里介绍的令人敬畏的编码流程 - 顺序,条件和循环/迭代流程。


小测验的答案


在下面 Python 代码片段中找到其中的编码流程。


  • Sequential flow 顺序流


  • Iteration 迭代

    while 语句


  • Conditional 条件

    if 语句


继续前进


现在你已经了解计算机编程最基本的概念。如果你想学更多东西,或者想把软件开发当作职业,那你还需要深入学习。有很多资源可以帮助你学习。知道如何根据自己的经验和水平挑选资源非常重要。


尽量不要被你朋友圈中比你更有经验的程序员所说的新词汇淹没。你可以使用一个便笺来记录这些术语,但不要急于搞明白它们的意思。深入学习和实践之后,你会赶上的。


这里有一些资源可以帮助你开始编码生涯,你可以从这里开始建立自己的编程基础:


  • 选择 Python 教程:

https://www.codecademy.com/ 


  • 免费的 Pluralsight 教程:

https://app.pluralsight.com/library/courses/what-is-programming/table-of-contents


注意:我不推荐过度阅读同样的主题。我更相信在将学到的东西付诸实践,也就是练习。因此我没在这里列出大量的资源供你学习。如果你已经不是初学者,可以尝试基于自己已经学到的东西,从 Google 搜索点别的东西来学习。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存