<commit_before>---
Title: 数据抽象
Date: 2017/8/22 2017-8-22 23:30:48
---

> 现在到了数学抽象中最关键的一步：让我们忘记这些符号所表示的对象。（数学家）不应该在这里止步，有许多操作可以应用于这些符号，而根本不必考虑它们到底代表这什么。                                      
————<NAME>, 思维的数学方式

SICP在我这似乎是一本永远读不完的书，一方面在于其难度，另一方面在于其不少的练习题，必须承认一点，可能确实是自己水平有限，每一道题对我而言都并不是那么容易的，几乎每道题都要经过仔细的思考才能做出来，面对这本书，我觉得更应当视其为一本数学书来对待，回忆下看数学教材时被其中的显然，易得等词汇爆掉脑袋然后怀疑人生的感觉吧，书中每一个知识点都有许多“小习题”等你来解决，但是，你真以为这些小习题能够轻松解决掉，那就太天真了，我曾经膝盖就中过一箭，这些小习题对我而言称之为时间黑洞也不为过，当然，或许真的有大神能把这些小习题快速刷掉吧，毕竟这个世界总是存在一些人能够如同数学教材作者那样，对“显然”，“易得”等这样的词汇有着相同感触。

做这些习题是不容易的，若是不做的话，这本书恐怕也读不出什么意义来。正是这种不易所带来的挑战感促使着自己不断去完成这些练习，尽管它们并不能对工作或实际项目带来太多直接的收益，但是却能让自己更清楚编程本质是什么，对自己所做的，称之为“编程”的活动有一个更高视角。

SICP第二章主要讲的数据抽象，相比第一章所讲的过程抽象而言，在这章中讲述了构造大型系统的一些重要方法，如，利用程序语言提供的粘合剂来进行复合数据的构造与表示，以及利用*闭包*性质和构造*抽象屏障*将复合数据进一步抽象的方法，通过这些方法的实践进一步阐述了大型系统的一些实际方法，如按类型分派方法，数据导向设计，消息传递设计等。  
在这一章里依旧是围绕抽象展开的，事实上，这本书都所说的一切或许可以概括为“**如何利用各种抽象方法来控制复杂**”，理解这本书作者所要表达的思想，就能更清楚的区透析整本书的知识。
有意思的是，书中每一章都会引述一些文字，每章读完后都值得回顾体会，或许会有新的体会，对本章对所要传达的思想将会有一个更深刻的认识。

首先回答一个问题，为什么要做数据抽象？有项目经验的人肯定深有感触，当我们肆无忌惮的使用着各种封装好的库时，这就是数据抽象所带来的好处，它使得我们可以在更高层次的视角来思考问题，而不需要关注更底层的实现和各种细节，我们所处的层次越高，所关注的对象就更少，就能把更多注意力放在必要的对象上，这就进一步减少了思维的负担，如果你在编写一个web应用，同时要考虑HTTP,TCP层的实现细节，那是非常令人头痛的事情。而实际上，在开发web应用时，非特殊情况下，也不应当过多去关注这些更底层的细节。对OOP语言的使用者而言，各种类的构造，继承，封装，多态这些都是非常自然的活动了，这就是在做数据抽象，我们抽象出的是对象这一实体。  

提高抽象的层次，使得我们可以在更高层次来处理各类数据，而不考虑各种细节问题，减少了我们思维的关注的对象，其实也就是控制了复杂的规模，使其易于掌控，数据抽象不仅是克服复杂性极其有效的一种方式，同时也为模块化提供了便利。
我们可以在已有的数据抽象上再去建立更高层次的抽象，这个过程中我们就需要去建立抽象屏障，这体现就是就是分层设计的思想，典型如TCP/IP协议族，这是一种非常强大的思想，首位计算机博士<NAME>曾说过，“软件开发中遇到的所有问题,都可以通过增加一层抽象而得以解决”，当然，这句话还有下联，所以对抽象层的取舍和权衡也是非常重要的。
> Any problem in computer science can be solved with another level of indirection,except of course for the problem of too many indirections.

第三章还介绍了函数式编程里一些典型的编程方法，如map，reduce，fold，filter等，
如今这些方法早已渗透到各个语言当中去了，如JS中Array就自带map,rudece,filter等方法，当我们级联的使用这些方法时，其背后体现其实是一种基于流的思想，正如信号处理工程师所做的那样。我们的原始数据流入一个转换器，经过处理再流入下一层转换器，经过层变换，筛选最终得出我们想要的结果。

在huffman编码树的实现过程中，揭示了数据对象的表示是如何深刻影响使用数据的程序的性能。
最后介绍了数据导向和消息传递的两种程序设计方法，有过OOP语言开发经验的人应该都非常熟悉了，各种继承，多态，重载的影子在里面，简单来说就是按类型分派。一些熟悉的概念在这里以最原初的形式展开，尽管这些特性早已植入了现代编程语言的特性内，但是深刻了解和认识这些特性最初的样貌是有意义的。

本章最值得深入思考的问题是过程和数据的差异，特别是在lisp这种具有quote操作符的语言里，数据和过程的界限变得非常模糊，事实上，不仅是在lisp里，在接近操作系统底层的汇编语言中，数据和过程（代码）也是模糊的，CS寄存器指向的就当做代码执行，DS寄存器指向的当做数据，所以某种程度上，数据和过程的差异取决于其解释者，这里扯远了，作者在这个问题中举了序对的例子(cons x y)，对于数据，我们在脑海中潜意识的为其建立某个形象的的东西，例如图结构，树，数组，等等。然而实际上我们认为某个数据对象是图或者树或者序对，并不因为我们脑海中的形象赋予的，而是其表现出来的过程和限制条件，也就是说*这些对象之所以是，并非是因为脑海中的形象，而是其表现出来的过程和限制条件*，序对之所是序对，并不是因我们脑袋里那个一对符号在一起的形象，而是序对可以用car，cdr操作取其中的x和y，以及可以用cons来构造这样的玩意，所以只要你能提供符合cons,car,cdr定义的三个过程，那就是序对。因此，对于某个数据对象，我们并不需要要求它按我们脑海里直观的形象排列着，这有点像鸭子类型的语言。

> “鸭子类型”的语言是这么推断的：一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子，那它就可以被当做鸭子。也就是说，它不关注对象的类型，而是关注对象具有的行为(方法)。

或许有点难以接受，但是仔细思考下本章开头引用的文字：
> 现在到了数学抽象中最关键的一步：让我们忘记这些符号所表示的对象。(数学家）不应在这里停步，有许多操作可以应用于这些符号，而根本不必考虑它们到底代表着什么东西。  
关于这个问题还有一个形象的例子，那就是邱奇数的表示，尽管我们不是数学家，但我们也在攀登抽象的阶梯。

<commit_msg>update post<commit_after>---
Title: 数据抽象
Date: 2017/8/22 2017-8-22 23:30:48
---

> 现在到了数学抽象中最关键的一步：让我们忘记这些符号所表示的对象。（数学家）不应该在这里止步，有许多操作可以应用于这些符号，而根本不必考虑它们到底代表这什么。                                      
————<NAME>, 思维的数学方式

SICP在我这似乎是一本永远读不完的书，一方面在于其难度，另一方面在于其不少的练习题，必须承认一点，可能确实是自己水平有限，每一道题对我而言都并不是那么容易的，几乎每道题都要经过仔细的思考才能做出来，面对这本书，我觉得更应当视其为一本数学书来对待，回忆下看数学教材时被其中的显然，易得等词汇爆掉脑袋然后怀疑人生的感觉吧，书中每一个知识点都有许多“小习题”等你来解决，但是，你真以为这些小习题能够轻松解决掉，那就太天真了，我曾经膝盖就中过一箭，这些小习题对我而言称之为时间黑洞也不为过，当然，或许真的有大神能把这些小习题快速刷掉吧，毕竟这个世界总是存在一些人能够如同数学教材作者那样，对“显然”，“易得”等这样的词汇有着相同感触。

做这些习题是不容易的，若是不做的话，这本书恐怕也读不出什么意义来。正是这种不易所带来的挑战感促使着自己不断去完成这些练习，尽管它们并不能对工作或实际项目带来太多直接的收益，但是却能让自己更清楚编程本质是什么，对自己所做的，称之为“编程”的活动有一个更高视角。

SICP第二章主要讲的数据抽象，相比第一章所讲的过程抽象而言，在这章中讲述了构造大型系统的一些重要方法，如，利用程序语言提供的粘合剂来进行复合数据的构造与表示，以及利用*闭包*性质和构造*抽象屏障*将复合数据进一步抽象的方法，通过这些方法的实践进一步阐述了大型系统的一些实际方法，如按类型分派方法，数据导向设计，消息传递设计等。  
在这一章里依旧是围绕抽象展开的，事实上，这本书都所说的一切或许可以概括为“**如何利用各种抽象方法来控制复杂**”，理解这本书作者所要表达的思想，就能更清楚的区透析整本书的知识。
有意思的是，书中每一章都会引述一些文字，每章读完后都值得回顾体会，或许会有新的体会，对本章对所要传达的思想将会有一个更深刻的认识。

首先回答一个问题，为什么要做数据抽象？有项目经验的人肯定深有感触，当我们肆无忌惮的使用着各种封装好的库时，这就是数据抽象所带来的好处，它使得我们可以在更高层次的视角来思考问题，而不需要关注更底层的实现和各种细节，我们所处的层次越高，所关注的对象就更少，就能把更多注意力放在必要的对象上，这就进一步减少了思维的负担，如果你在编写一个web应用，同时要考虑HTTP,TCP层的实现细节，那是非常令人头痛的事情。而实际上，在开发web应用时，非特殊情况下，也不应当过多去关注这些更底层的细节。对OOP语言的使用者而言，各种类的构造，继承，封装，多态这些都是非常自然的活动了，这就是在做数据抽象，我们抽象出的是对象这一实体。  

提高抽象的层次，使得我们可以在更高层次来处理各类数据，而不考虑各种细节问题，减少了我们思维的关注的对象，其实也就是控制了复杂的规模，使其易于掌控，数据抽象不仅是克服复杂性极其有效的一种方式，同时也为模块化提供了便利。
我们可以在已有的数据抽象上再去建立更高层次的抽象，这个过程中我们就需要去建立抽象屏障，这体现就是就是分层设计的思想，典型如TCP/IP协议族的设计，这是一种非常强大的思想，首位计算机博士<NAME>曾说过，“软件开发中遇到的所有问题,都可以通过增加一层抽象而得以解决”，当然，这句话还有下联，所以对抽象层的取舍和权衡也是非常重要的。
> Any problem in computer science can be solved with another level of indirection,except of course for the problem of too many indirections.

第三章还介绍了函数式编程里一些典型的编程方法，如map，reduce，fold，filter等，如今这些方法早已渗透到各个语言当中去了，如JS中Array就自带map,rudece,filter等方法，当我们级联的使用这些方法时，其背后体现其实是一种基于流的思想，正如信号处理工程师所做的那样。我们的原始数据流入一个转换器，经过处理再流入下一层转换器，经过层变换，筛选最终得出我们想要的结果。

在huffman编码树的实现过程中，揭示了数据对象的表示是如何深刻影响使用数据的程序的性能。最后介绍了显示按类型分派、数据导向（查表）、消息传递（SmallTalk 中消息传递的 OOP 设计思路， 又称dynamic dispatching动态分配的技术），有过OOP语言开发经验的人应该都非常熟悉了，各种继承，多态，重载的影子在里面。当然，现实中还存在着不同类型进行互相操作的情况，这就涉及到类型塔和强制转换问题，类型如何在不同层次上提升和下降，因为数据在传递和使用的过程中可能跨过不同的抽象层次，典型如TCP/IP协议族（数据一层一层剥开解析，再一层一层的包装发出），也提到了塔式结构的不足，这也是一个值得深入思考的问题，涉及哲学上的本体论。一些熟悉的概念在这里以最原初的形式展开，尽管这些特性早已植入了现代编程语言的特性内，但是深刻了解和认识这些特性最初的样貌是有意义的。

本章最值得深入思考的问题是过程和数据的差异，特别是在lisp这种具有quote操作符的语言里，数据和过程的界限变得非常模糊，事实上，不仅是在lisp里，在接近操作系统底层的汇编语言中，数据和过程（代码）也是模糊的，CS寄存器指向的就当做代码执行，DS寄存器指向的当做数据，所以某种程度上，数据和过程的差异取决于其解释者，这里扯远了，作者在这个问题中举了序对的例子(cons x y)，对于数据，我们在脑海中潜意识的为其建立某个形象的的东西，例如图结构，树，数组，等等。然而实际上我们认为某个数据对象是图或者树或者序对，并不因为我们脑海中的形象赋予的，而是其表现出来的过程和限制条件，也就是说*这些对象之所以是，并非是因为脑海中的形象，而是其表现出来的过程和限制条件*，序对之所是序对，并不是因我们脑袋里那个一对符号在一起的形象，而是序对可以用car，cdr操作取其中的x和y，以及可以用cons来构造这样的玩意，所以只要你能提供符合cons,car,cdr定义的三个过程，那就是序对。因此，对于某个数据对象，我们并不需要要求它按我们脑海里直观的形象排列着，这有点像鸭子类型的语言。

> “鸭子类型”的语言是这么推断的：一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子，那它就可以被当做鸭子。也就是说，它不关注对象的类型，而是关注对象具有的行为(方法)。

或许有点难以接受，但是仔细思考下本章开头引用的文字：
> 现在到了数学抽象中最关键的一步：让我们忘记这些符号所表示的对象。(数学家）不应在这里停步，有许多操作可以应用于这些符号，而根本不必考虑它们到底代表着什么东西。  
关于这个问题还有一个形象的例子，那就是邱奇数的表示，尽管我们不是数学家，但我们也在攀登抽象的阶梯。


{'fraction_non_alphanumeric': 0.08821537419573315, 'fraction_numerical': 0.006772773450728073, 'mean_word_length': 22.255905511811022, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6695883', 'n_tokens_mistral': 5502, 'n_tokens_neox': 6364, 'n_words': 106}