- 内容提要
- 序 1:程序里的世界
- 序 2:最后一层表象
- 关于本书
- 致谢
- 引言:简单的本源
- 篇一:计算系统
- 第 1 章 数,以及对数据的性质的思考
- 第 2 章 逻辑
- 第 3 章 抽象
- 篇二:语言及其面临的系统
- 第 4 章 语言
- 第 5 章 从功能到系统
- 篇三:程序设计的核心思想
- 第 6 章 数据结构:顺序存储
- 第 7 章 数据结构:散列存储
- 第 8 章 执行体与它在执行过程中的环境
- 第 9 章 语法树及其执行过程
- 第 10 章 对象系统:表达、使用与模式
- 篇四:应用开发基础
- 第 11 章 应用开发的背景与成因
- 第 12 章 应用开发技术
- 第 13 章 开发视角下的工程问题
- 第 14 章 应用程序设计语言的复杂性
- 篇五:系统的基础部件
- 第 15 章 分布
- 第 16 章 依赖
- 第 17 章 消息
- 第 18 章 系统
- 篇六:系统的基本组织方法与原理
- 第 19 章 行为的组织及其抽象
- 第 20 章 领域间的组织
- 附一:主要编程范式 及其语言特性关系
- 附二:继承与混合,略谈系统的构建方式
- 附三:像大师们一样思考——从 UML 何时死掉 谈起
- 附四:VCL 已死,RAD 已死
15.5 在结构化的思维框架下,函数拆分的可能求解
如前所述,函数的拆分与数据的拆分有一定的关系。总的来看,若函数 A 与函数 B 之间没有时序依赖,则函数 A 与函数 B 能否拆分取决于它们所处理的数据是否能拆分或复制(映像)。
根据函数本身的结构化性质,当某个函数拆分成函数 A 与函数 B 时,必然是三种逻辑结构所映射的关系。进一步地,它们对数据拆分的需要也各有不同。
其一,顺序结构意味着函数 A 与函数 B 可以使用数据的映射(的部分或全部)。例如下面的代码:
1 2 3 4 5 6 7 8 9 |
|
foo()
函数持有了 a、b、c 三个数据的全集,并且我们假设——事实上我们是特意这样构造的——函数的两个子步骤(代码 6、7 行)之间没有时序依赖。那么我们可以将 a、b、c 映射为两个数据,并在各自的子函数中使用它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
在示例 1 中使用“ var
”声明的数据总量被拆分成示例 2 中的两个对象,由于示例 1 中的两个步骤之间是顺序关系,因此它们可以分别使用示例 2 中的两个 data
,即“数据总量的映射”的部分 7 。
其二,分支结构(以及多重分支)类似于顺序结构,函数 A 与函数 B 可以使用数据的映射(的部分或全部),但是函数 A 与函数 B 相对于条件判断逻辑都存在“(逻辑的)时序依赖”。例如:
1 2 3 4 5 6 7 8 9 10 |
|
在这个 foo()
示例中,函数的两个分支之间是没有时序依赖的,但是它们都必须在x这个逻辑之后执行。由于x相对于两个分支不存在——或可以不存在——数据依赖关系,因此两个分支也可以持有各自的 data。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
请注意一个有趣的事实:示例 4 所使用的 foo_1()
和 foo_2()
,与示例 2 中是完全一致的。这意味着这两个逻辑以及相关的数据,与其外在的其他逻辑无关。这体现了它们的可分布性,即可拆分与可处理。
在示例 4 中,对于 foo()
函数来讲, foo_1()
与 foo_2()
相对于 x 这个逻辑都存在“逻辑上的”时序依赖,但它们之间以及它们之于 x 的数据,都不存在依赖。
其三,循环结构意味着函数 A 与函数 B 使用数据全集,或其整体的单一映像。例如:
1 2 3 4 5 6 7 8 9 |
|
首先,一种错误的理解在于将 5、6 两行代码视作不存在依赖的两个子过程,进而做这样的处理:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
尽管在这样的逻辑中, foo_1()
与 foo_2()
是可以持有数据的部分或部分映像的。但这与我们在这里讨论“循环逻辑”的初衷是相背离的。
我们事实上是在讨论将“一个具有循环逻辑性质的函数”拆分为多个子函数的情况。我们的目标是找到与“循环逻辑”这一性质相关的数据处理方案,而非将循环逻辑映射为多个却无视该逻辑之于数据的关系。在上述方案中,循环之于数据的性质是没有丝毫变化的。
将“循环逻辑本身”拆分开来,其基本含义是循环项次的展开。也就是说,我们能够将 100 次循环变成两个 50 次,或者 100 个 1 次。我们讨论的是这 50 次或 1 次中的数据之于“循环项次的展开”的逻辑间的关系。然而关于这一问题的答案是简单的:每一个循环项次,都必然面临数据的全集,或其全集的映像。因为 5、6 两行代码在时间上——可以理解为在一个时间区段中——是关联的,而“循环项次的展开”只是将时间区段趋向无限小的分隔,而并没有将上述这一关联关系解构。
以函数式语言的处理为例,我们可以将上述逻辑变成一个基于函数参数界面的递归,例如:
1 2 3 4 5 6 7 8 9 10 11 |
|
我们应该注意到,仅以“循环逻辑的展开”而言,函数 foo_x()
的任意一个实例都只依赖调用界面上的i值。而这个i值是一个循环过程中的中间值,或是一个传入的确值,都是与这个函数无关的。因此,任意递归函数的单一实例,对于“循环逻辑”都是透明的。
然而再观察上述的示例 7,我们发现函数 foo_x()
的任意一个实例,无论它仅是一个单次递归,或是分布到其他计算环境中的一个迭代区段,它都必将面临整个数据全集:
1 |
|
一种较好的、较可行的方案是将这个数据全集也放在函数的参数界面上 8 。例如:
1 2 3 4 5 6 7 8 9 10 |
|
这样带来的结果是: foo_x()
的执行可以被分布,但其“所有分布(的各个服务之间)”存在着逻辑之于数据全集的关联。在现实中,这一分布带来了逻辑向计算系统迁移的可能性,即一个大的循环过程可以分布在多个计算系统中完成,因而仍然是非常重要的大型系统下的分布解决方案。
但是整个循环逻辑与其占用的时间区段的总量并没有变化。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论