什么是计算机思维?2个简单表达式让你理解!

0 前言

本文以十进制整数为例,使用2个最简单的表达式char a = -20;char c = a + b;,为你深入浅出地讲解计算机思维,力求将抽象的计算机思维具象化讲解,同时,我将为你描述一个宏大的计算机世界的蓝图


计算机思维与核心思想概要:

  • 人类世界与计算机世界的通道:编码与译码
  • 计算机处理信息的本质:二进制信息的运算
  • 现实世界向计算机世界的映射:二进制信息运算的原则
  • 人类与计算机斗争的胜利与妥协:分而治之、抽象思想与分析程序行为、分工协作

为了避免内容的冗杂,本文只谈及十进制整数和二进制数,力求使用最浅显易懂的知识,讲清楚高深的计算机思维

1 人类世界与计算机世界的通道:编码与译码

本小节,从最简单的十进制整数与二进制数的相互转化,来解释编码与译码

1.1 一段简单的C语言代码

你需要使用VS 2017进行单步调试和查看内存。

补充链接:使用VS 2017进行单步调试和查看内存

这里我们用char代表能够存储一字节大小的机器数的容器,并不使用其字符的功能。
存储1字节
你可以想象,这是一个char类型的容器,能够存储8位二进制数

以下是我们即将展开讲解的C语言代码:

#include <stdio.h>
#include <Windows.h>

int main() {
	char a = -20;
	unsigned char b = -20;

	system("pause");
	return 0;
}

1.2 查看变量的内存情况

首先,我们查看一下数据ab在内存中的情况,这个时候,计算机已经完成了十进制整数到二进制的转换,内存中存储的是对应的二进制信息,通常采用十六进制表示。

变量a在内存的情况:
内存
变量b在内存的情况:
内存
如果你不知道如何查看变量地址的话,可以通过C语言中的&a&b,输出到控制台来查看。

我们可以看见,不管是带符号的变量a还是不带符号的变量b,将-20赋值给它们,存储到内存中的,都是十六进制的ec,也就是二进制的11101100

这里表明,计算机是先将-20转换为11101100,然后再放入变量中的。

我们查看一下【局部变量】:
局部变量
这里表明:

  1. a和b的二进制信息是完全一样的
  2. 他们输出到屏幕的数据类型和十进制数值不一样的

这是为什么呢?

1.3 计算机编码:从十进制到二进制

我们再看下面的图
转换

首先,我来解释一下从通过键盘输入存储存储器的过程

  1. 从屏幕键入-20,计算机先进行编码,变成了二进制的11101100
    编码
  2. 再将二进制信息,存储了变量a和b中。其中,变量a和b分别是带符号和不带符号的char类型数据
    两个盒子
    这里有两个不同颜色的盒子
  • 它们分别代表char类型和unsigned char类型
  • 他们分别被打上了标签ab
  • 他们都存入了二进制信息11101100

1.4 计算机译码:从二进制到十进制

下面我来讲解一下,从二进制信息输出十进制的过程。

尽管a和b两个盒子中的二进制信息完全一样,但是你要清楚,这只是在计算机世界完全一样,在人类世界中,二进制信息的含义应该是:二进制信息的含义 = 位 + 上下文,所谓上下文,就是二进制信息所处的环境。

参考学习:《深入理解计算机系统》的1.1节

也就是说【11101100 + 数据类型】,通过这两个条件,才能正确得到现实世界中,二进制信息的含义,这里我们将二进制,以十进制形式进行输出。

转换

  • 对于变量a,以带符号的十进制形式输出,因为它被转换为二进制之前,就是带符号的十进制数。
  • 对于变量b,以无符号的十进制形式输出,-20以补码形式转换成二进制数,然后再存入b中,此时这个二进制数的含义发生了改变,因为它的上下文从带符号数变成了无符号数。

这里我们需要强调的是,对于十进制与二进制的相互转换,大多情况下,是怎么进去的,就要怎么回来,除非特殊需求,比如需要将十进制数转换为字符输出,这也是允许的。

1.5 小结

本节我为你展示了两个过程

  • 十进制 --> 二进制,这个过程叫编码
  • 二进制 --> 十进制,这个过程叫译码

编码与译码

计算机的外部设备,鼠标、键盘、音响和显示器等等,这些都是从人类世界进入计算机世界的入口

人类通过这些外部设备输入信息(十进制数、文字、图片、视频、音频……),由计算机进行编码,以二进制数的形式进入计算机世界,你也可以理解为入乡随俗

这些二进制数在计算机的世界里遨游,有些二进制数是数据,有些是指令,这些指令是计算机世界的指挥者,他们指挥着数据工作,这些数据可能进行加法工作,可能进行乘法工作……

当这些二进制数,完成了他们的探索使命,就要回到人类世界,这个时候,他们将会通过计算机进行译码,恢复他们在人类世界的本来面貌。

虫洞
你可以想象,一架宇宙飞船,从人类世界,通过虫洞进入计算机世界,在完成任务后,又通过虫洞返回人类世界。

2 计算机处理信息的本质:二进制信息的运算

2.1 体验计算机的二进制运算

我们把上一小节的代码,再加上一行代码char c = a + b;

char a = -20;
unsigned char b = -20;
char c = a + b;

我们查看一下内存和局部变量:
内存
内存
可以得知,变量c对应的十六进制是d8,二进制是11011000,又因为它是char类型,因此二进制信息对应的十进制信息是 -40

这里你是不是有疑问?试想,如果是按照十进制进行相加-20 + 236,肯定不等于-40,并且,一个char类型的数和一个unsigned char类型的数据,不同的数据类型相加,又是怎么进行的呢?

这里,也就可以谈及我们的要点信息处理的本质,是二进制信息的运算。我们所有的信息,都是先转换成二进制信息,再进行相关运算,运算结束后再根据其上下文,返回对应的结果。
人类世界与计算机世界
我们根据这个过程,再来审视一下刚才的例子:

  1. ab的内部是二进制信息,将它们进行加法运算,得到结果
  2. 将结果放进容器c
  3. c中的二进制信息,再由计算机译码,转换为十进制输出到屏幕

转换

如果我们将最后一条加法改成unsigned char c = a + b;,c的结果将会是216,具体过程留给读者思考。

需要注意的是,这里我举这个例子,只是为了说明本文的观点,事实上,C语言编程中,非常不建议将无符号与有符号数混合运算,因为那样得到的结果很可能令人费解,可能也没有什么现实含义。

2.2 小结

本小节为你讲述了这些内容:

  1. 对于char a = -20;,计算机先将-20以补码的方式转换为二进制信息11101100,在将其放入变量a所对应的内存中。
  2. 对于char c = a + b;,计算机会在CPU中,会进行如下过程
    1. 将a和b的二进制信息进行加法运算
    2. 将得到的结果放进变量c所对应的内存中
    3. 再转换为十进制显示到屏幕上

二进制代码
在计算机的世界里,只有二进制数,这些二进制数,在计算机世界中,按照人类设定的规则不断地运算,完成任务后,又以原本的身份回到人类世界。

这,就是计算机处理数据的本质,计算机按照人类设定的规则进行二进制运算

推荐阅读《编码:隐匿在计算机软硬件背后的语言》

3 人类世界向计算机世界的映射:二进制信息运算的原则

计算机是人类发明的工具,是人类智能的延伸,因此,计算机世界的运算法则,要遵从人类世界的运算法则,才能更好地为人类服务。

例如人类世界中,1+2 = 3,那么在计算机世界中,0001 + 0010也要等于0011

无论是加减乘除法,还是其他种种,都有遵循这个原则,这样人类才能更方便地利用计算机解决现实问题。

因此,二进制数本质上就是现实世界在计算机世界的映射,它具备现实世界的含义。他们之间实现映射的通道是编码与译码

我给出你一个表格,做个对比,让你清晰地感受这种映射

人类世界计算机世界
领导者控制器
基层员工运算器
能源物质数据

给你解释一下:

  • 控制器在CPU内,能够指挥计算机怎样工作
  • 运算器在CPU内,能够进行数据的运算
  • 数据在计算机存储器(内存、硬盘等)中四处游走

给你推荐一个动画,能够让你清晰感受到:人类是高维生物,而计算机世界是人类创造的一个新的世界,这个世界是人类世界的映射,它为人类世界工作和服务

推荐动画:《瑞克和莫蒂》第二季第六集:电池微世界

4 人类与计算机斗争的胜利与妥协

自从计算机被发明以来,人类与计算机一直在进行着永无休止的斗争,不断进行着碰撞与融合

计算机是人造的产物,它是人类思维的产物,但是随着它的不断发展,其复杂性成指数级上升,独立的个体已经很难去控制它,因此,为了充分利用计算机来为人类造福,人类又发明了很多方法去控制它,去约束它。

计算机诞生以来,形成了很多的分支领域,也诞生了很多优秀的理论,这些都让人类能够更好地控制计算机,为人类造福,例如软件工程中的瀑布模型及其衍生模型、敏捷开发方法;又例如当今时代的火热技术人工智能、大数据;又例如面向未来的量子计算机……

人类在这场没有硝烟的战争中,有胜利,也有妥协

4.1 人类的胜利

人类在这场战争中,充分发挥自身的智能,成功地使用分而治之的哲学思想逻辑抽象的数学思想,战胜了复杂的计算机!

4.1.1 分而治之思想的应用

根据本文的核心理念,这里我只使用文章开始提及的简单表达式,来为你讲解分而治之思想的实际应用,然后给到你该思想的本质内核。

对于表达式char a = 3;,我们可以将其分开看待

  1. 3转换为二进制,看作0011
  2. char看作某种类型的容器
  3. a看作容器的标签

这样,你就可以将这个表达式看成:将0011放进贴着标签achar类型的容器中。

放入容器

这个简单问题,可能没有让你感受到复杂问题的简化,你可以想象一个需要你解决的问题:让你开发一款操作系统,这个问题足够复杂,但是其开发过程的各个环节,都渗透着分而治之的思想,我想你应该清楚它的重要性了。

该思想能够让复杂性问题简单化,让很难被解决的问题得以解决,事实上,人类面对大规模问题,都会采取这样的思想:软件工程的分工协作、硬件编程的层次建模……它有大量的应用场景,这个思想对读者来说,是年金,它每年都会给读者分红,时间越久,利息越多

4.1.2 分而治之思想的本质

Divide et impera ——拉丁语,意为“分而治之”,引自Machiavelli的一句政治箴言,1532

分而治之的思想本质,就是拆解与转化

  • 将大问题拆解为小问题
  • 将小问题拆解为更小的问题
  • 将更小地问题转化已知的被解决过了的问题

Critical thinking(批判性思维)的相关资料指出:所谓创造,就是将已知的产物以全新的方式结合起来。

而分而治之的思想,正是通过问题拆解,转为已知,再综合结果的方式,实现了创造

切开的蛋糕
如果蛋糕太大一口吃不下,那就先切开它!然后一快一快吃下它!

期待你遇到大规模问题不要惊慌失措,你可以先拆解他、再转化它、最后综合起来,几乎任何问题都可以被解决。

4.1.3 抽象与实现

逻辑抽象是人类智能的体现,它是计算机所不具备的,面对一个复杂的问题,我们可以对其进行不同层次的抽象、建模,这些行为的进行,可以在人的脑子中,也可以在纸面上,或者在其他工具上。

根据抽象得到的结果,人类再使用计算机这个得力助手去实现它,这样就能真正地解决现实问题。

计算机领域的抽象与实现,可以是 [数据结构] 的抽象数据类型;可以是 [Verilog HDL] 的层次建模思想;可以是软件工程的UML建模……

抽象与实现
抽象与实现的思想,其实渗透到了人类生活的各个领域,期待你能够以这样的方式去重新审视这个世界。

4.2 人类的妥协

人类不会向计算机屈服,但是面对一些挑战,也不得不做出适当的妥协让步,这也是为了更好的应用计算机高效率地解决现实问题。

4.2.1 分析计算机行为快速搞定bug

当程序没有按照你预期的结果执行,不要懊恼,不要悲伤,更不要想着:“为什么它没有按照我想的去做?”

这样的想法,只会让你更加懊恼和沮丧,你应该想的是:是不是我哪里没有描述清楚,所以它没有做好事情?

你应该做的是:按照程序执行的行为,使用单步调试/断点调试,逐一分析,直到找到问题根源

计算机是个听话的孩子,它只会按照你告诉它的去执行,因此,如果它出错了,一定是你指挥失误,你只需要分析它的每一步行为,就能够以最快的速度找到错误,解决bug

这是计算机历史上,导致计算机崩溃的一只虫子(bug),让我们一起,按照程序的行为,找到它,消灭它!

4.2.2 大规模项目的分工协作

随着计算机的不断发展,大规模复杂问题开始出现,独立的个体可以写出一个小程序,但是面对大规模问题,人类就如同《焦油坑》里的动物一样,不断挣扎,但是越挣扎,反而越痛苦。
焦油坑
直到软件工程出现,人类开始学会分工协作,共同应对棘手的“焦油坑”,成立了“外科手术队伍”,具备了解决大规模问题的能力。

推荐阅读:《人月神话》
你将会更加直观地感受到人类与计算机的斗争,比如焦油坑、外科手术队伍……

5 期待你更好地在计算机世界遨游

本文从一个最简单的表达式出发,为你描述了人类世界与计算机世界的宏大蓝图;给你讲述了重要的计算机思维,并将其具象化表达;给你讲解了重要的破解复杂问题的方法和适当妥协让步的策略。

计算机是人类智慧的延伸,是人类得力的工具,是人类的 “外包大脑”,永远记住,你不是程序员,不要当程序员,你是解决问题的人你要利用人类特有的智能,这,才是你与计算机的不同,你能做到计算机做不到的事情,你要充分利用计算机这个得力工具去解决问题,不要被其反噬控制。

期待此文,能够为你带来全新的计算机世界观,让你更好地开启计算机旅程。


声明:

  1. 本文大部分图片均为自己制作,一些图片是我购买的具备个人版权的图片,还有极少数素材来源于网络,侵删。
  2. 本文中,部分的类比和比喻,伴有科幻主义色彩,非技术部分的内容可能并非严谨的科学事实,请勿恶意揣测,谢谢。
  3. 本文涉及的技术,为了方便你理解,采用了简化模型,并未展现完整的技术细节,技术细节你可以从我推荐的书籍中学到。
  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
中缀表达式是人类最常使用的数学表达方式之一,但是对于计算机来说,中缀表达式并不方便进行计算,因此需要将中缀表达式转化为抽象语法树,使得计算机能够更方便地进行计算。本文将介绍中缀表达式转化抽象语法树的原理、方法和实现过程。 一、中缀表达式的定义与表示 中缀表达式是指运算符位于两个运算对象之间的表达式。例如,2 + 3就是一个中缀表达式。中缀表达式的优点在于它符合人类的思维方式,但是对于计算机来说,中缀表达式需要进行转化才能进行计算。 中缀表达式可以用字符串的形式表示,例如:"2 + 3"。字符串中的运算符和数字之间需要用空格隔开,否则会出现歧义。例如,"2+3"既可以表示中缀表达式,也可以表示其他含义。 二、抽象语法树的定义与表示 抽象语法树是指将代码中的语句和表达式转化为树状结构的一种方式。在抽象语法树中,每个节点都表示代码中的一个语句或表达式,节点之间的关系表示语句和表达式之间的层次结构和依赖关系。例如,一个加法表达式的抽象语法树如下所示: + / \ 2 3 在抽象语法树中,每个节点都有以下属性: - 类型:表示节点所表示的语句或表达式的类型; - 值:表示节点所表示的语句或表达式的值; - 子节点:表示节点的子节点,即该节点所依赖的语句或表达式。 抽象语法树可以用树状结构表示,例如: + / \ * - / \ / \ 2 3 4 5 三、中缀表达式转化为抽象语法树的方法 将中缀表达式转化为抽象语法树的方法可以分为两个步骤:将中缀表达式转化为后缀表达式,然后将后缀表达式转化为抽象语法树。 1. 将中缀表达式转化为后缀表达式 将中缀表达式转化为后缀表达式方法是使用栈。从左到右扫描中缀表达式中的每个元素,如果是数字,则直接输出;如果是运算符,则判断该运算符与栈顶运算符的优先级,如果该运算符优先级较低,则弹出栈顶运算符并输出,直到该运算符优先级大于栈顶运算符或者栈为空,然后将该运算符入栈。如果是左括号,则直接入栈;如果是右括号,则弹出栈中元素并输出,直到遇到左括号为止。最后,将栈中所有元素弹出并输出。 例如,将中缀表达式"2 + 3 * 4 - 5"转化为后缀表达式的过程如下: 中缀表达式:2 + 3 * 4 - 5 后缀表达式:2 3 4 * + 5 - 2. 将后缀表达式转化为抽象语法树 将后缀表达式转化为抽象语法树的方法是使用栈。从左到右扫描后缀表达式中的每个元素,如果是数字,则将该数字作为叶子节点插入到树中;如果是运算符,则弹出栈顶两个元素作为该运算符的左右子节点,并将该运算符作为根节点插入到树中。最后,栈中剩余的元素即为树的根节点。 例如,将后缀表达式"2 3 4 * + 5 -"转化为抽象语法树的过程如下: - / \ + 5 / \ 2 * / \ 3 4 四、实现过程 将中缀表达式转化为抽象语法树的实现过程可以分为以下几个步骤: 1. 将中缀表达式转化为后缀表达式; 2. 将后缀表达式转化为抽象语法树; 3. 输出抽象语法树。 例如,使用Python实现将中缀表达式转化为抽象语法树的代码如下: ```python class Node: def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right def infix_to_postfix(expression): stack = [] postfix = [] for char in expression: if char.isdigit(): postfix.append(char) elif char == '(': stack.append(char) elif char == ')': while stack and stack[-1] != '(': postfix.append(stack.pop()) if stack and stack[-1] == '(': stack.pop() else: while stack and stack[-1] != '(' and precedence(char) <= precedence(stack[-1]): postfix.append(stack.pop()) stack.append(char) while stack: postfix.append(stack.pop()) return postfix def postfix_to_tree(postfix): stack = [] for char in postfix: if char.isdigit(): stack.append(Node(char)) else: right = stack.pop() left = stack.pop() stack.append(Node(char, left, right)) return stack.pop() def print_tree(tree, level=0): if tree: print(' ' * level + str(tree.value)) print_tree(tree.left, level + 1) print_tree(tree.right, level + 1) def precedence(operator): if operator == '+' or operator == '-': return 1 elif operator == '*' or operator == '/': return 2 else: return 0 expression = '2 + 3 * 4 - 5' postfix = infix_to_postfix(expression) tree = postfix_to_tree(postfix) print_tree(tree) ``` 五、总结 将中缀表达式转化为抽象语法树是一种常见的算法,它可以方便地进行计算机计算。本文介绍了中缀表达式的定义和表示、抽象语法树的定义和表示、转化方法和实现过程。通过本文的介绍,读者可以更好地理解中缀表达式转化抽象语法树的原理和方法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

XV_

感谢您的认可,我会继续努力!

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

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

打赏作者

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

抵扣说明:

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

余额充值