写代码如盗墓笔记,如何逃出生天? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
muchan92
V2EX    程序员

写代码如盗墓笔记,如何逃出生天?

  •  5
     
  •   muchan92 2021-02-05 09:21:06 +08:00 4577 次点击
    这是一个创建于 1707 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为什么说写代码如盗墓笔记呢,是因为写代码确实有一些特点和盗墓笔记很像,比如坑比较多,也都处于暗处,没有在明面上,在进入之前是无法知晓整个结构的,只有亲身深入其中,详细探索完每一个地方之后才能知晓。

    但是这个探索过程是非常艰难的,因为一切都是未知的,也没有任何导航可用,只能两眼抹黑的往前瞎拱,自然免不了各种迷路,各种碰壁,各种掉坑。

    这种艰难的状况经历了多了之后,我们就应该思考了,是什么原因导致了这种状况?那么又有没有方法可以解决这种状况吗?

    分析代码的特点

    我们重新审视分析下程序代码的特点,只有了解了这些特点,才能探究出原因。
    程序代码主要是由数据变量和逻辑控制流构成的,其中逻辑控制流占据了绝大部分。而逻辑控制流包含了许多分支,就像本来是一条道路,却分叉出了许多支路,选择一条支路,走着走着又继续分叉出更多的支路出来,密密麻麻,乱乱糟糟的,这就导致了这些道路非常难走。并且更令人抓狂的是,这些道路没有导航,没有地图,没有路标,非得抹黑走到路的尽头才能知道尽头到底是什么。
    数据变量基本上有两个职能,一个是用来标记逻辑控制流的运行状态;另一个就是用来组成数据结构。而实际上离散的数据变量也算是数据结构的一种,只是不同于经典数据结构,不太明显而已,容易让人误认为其仅是一个离散的变量,而忽略其影响并左右逻辑控制流的结构性的意义。

    所以整体看来就是,数据变量和逻辑控制流是互相关联互相影响的。

    而从分析过程中也可以知道,程序代码的特点就是分支散碎,藏于尽头,难以获知。导致这一特点的原因也是逻辑控制流的复杂性、散碎、藏于尽头。也即,程序代码的这些不好的特点都是由逻辑控制流体现的。

    那么逻辑控制流能避免这些缺点吗?答案是不能。因为这是由于逻辑控制流的根本复杂性和本质复杂性所决定的。逻辑控制流说白了就是平常说的过程,所以也是无法跳过的,因为你不能跳过过程而直接获得结果。
    所以从这里看来,程序代码的这些缺点也依然存在,无法避免。

    什么是数据流向?

    不过有一点非常值得思考,逻辑控制流的目的是什么?是进行数据转换,也就是进行数据结构的转换,也就是描述并控制数据的流向。
    这也是程序的本质:从输入数据结构,来获取输出数据结构。
    所以,写逻辑控制流的意义不是纯粹为了写逻辑控制流,而是为了进行数据结构的转换,描述并控制数据的流向。因此,我们可以获得一个非常重要的有用信息,只要能够理清数据的流向,就能显著降低阅读代码的难度。 我们阅读代码的目的其实也就是为了弄清楚数据的流向。

    那么我们就把重心转移到理清数据的流向上来,分析下如何才能更好的理清数据的流向。
    因为逻辑控制流的本质复杂性,所以我们无法通过逻辑控制流来清晰的理清数据的流向,这也正是我们一直以来读写代码所采用的方式,事实证明,这不是好的方式,数据流向散落在逻辑控制流的各个地方,根本没有好的办法可以串联起来,形成一个有导航意义的地图。

    前文说过,程序代码是由数据变量和逻辑控制流构成的,也提到过,数据变量和逻辑控制流是互相关联互相影响的,并且数据流向自然也离不开数据变量。既然没有好的办法通过逻辑控制流来理清数据流向,那么就需要思考下在数据变量上是否有好的办法。

    数据变量是什么?前文解释过,数据变量就是数据结构。
    请着重注意 结构 这个概念,结构表示了层级和关联,而关联就是流向,我们正是想理清流向。所以在数据结构身上,我们看到了理清数据流向的希望。

    在开始分析如何通过数据结构来理清数据流向之前,我觉得有必要,也非常有必要重新强化对结构的认知。因为有太多的小伙伴们对结构没有足够的重视,不以为然,莫不关心,没有认知到结构的重要性以及不可或缺性以及根本本质性。

    结构是“神”的语言

    我们眼前的一切,周围的一切,手机、电脑、衣食住行、山川大海、以及你我,都是由结构构成的,更确切的讲,这一切就是结构。我们第一次认知这个世界就是从结构开始,第一次睁眼看到父母,第一次拆解玩具,第一次感受花草。人类学习结构,利用结构,组成各种各样的新的结构、新的工具来帮助我们生活。人类研究周围的各种物体,以及日月星辰、宇宙天体,其实就是在探索它们的结构,和结构之间的关联。只有知晓了它们的结构与之间的关联,我们才能组装更高级的结构来生产工具,才能进一步知晓周围万物的原理和世界的运行规律。

    我们都知道我们身处的宏观世界是三维的立体世界,这个世界中的任何物体都有其形状,有其结构,形状就是结构,没有结构将无法存在于这个世界中。即使加入看不见摸不着的时间维度组成四维的时空世界,结构依然存在于任何一个维度之中。我们都知道万有引力,其中引力并不是虚无飘渺的能量,而是质量在时空中引起的时空扭曲,正是因为时空可以被扭曲,所以任何一个维度都是有结构的,人类一直在探索其中的结构和关联。

    在微观世界中,物质是由分子构成的,人类对分子的结构进行探索发现了原子,对原子的结构进行探索发现了原子核,继续不断对结构进行探索发现了夸克。反过来讲就是,各种更小的粒子结构通过 关联 组合成较大的粒子结构,层层组合最终构成了宏观世界。化学的本质是物理的粒子运动,粒子运动其实是结构之间的关联,人类的情感、想法等都是细胞间的化学信号,而化学信号又是物理的粒子运动,本质又是结构之间的关联,所以我们人类的一切行为,生物的一切行为都是结构之间的关联。

    所以,结构是“神”的语言,用结构和结构之间的关联来描述整个世界。

    分析如何理清数据流向

    数据结构之于程序代码也同样如此,其是程序的根本,数据结构与之间的关联描述了整个程序世界,逻辑控制流依存于数据结构,若离开其,则程序将崩塌不复存在。

    现在我们开始分析如何通过数据结构来理清数据流向。
    由前文可知,数据的流向是由逻辑控制流来控制的,但是逻辑控制流本身就是复杂的,现在我们换个说法,数据的流向是由逻辑控制流来驱动的,逻辑控制流只是通过一些条件、规则来驱动数据进行流动,其流向并不是在写逻辑控制流的过程中而突然打通的,而是在写逻辑控制流之前,在数据结构定义的时候,就已经开始设计构思其流向了。只是几乎我们每一个程序员都忽略了这一点,我们错误的以为数据流向是存在于逻辑控制流之中的。

    这种误解是如何产生的?

    首先,程序的原作者在定义数据变量也即数据结构的时候,就已经在脑中设计构思其流向了,但他并没有在该数据结构上做说明来描述数据的流向。然后,他认为数据的流向应该用逻辑控制流来描述就可以,就够用了,所以就零零散散的用代码在逻辑控制流中进行描述。这就导致了别人不得不完整阅读逻辑控制流中的散碎的、反人类的代码来弄清楚数据的流向,原作者留给后来人的仅有这一堆蜿蜒曲折,支离破碎的代码,而清晰的导航地图只留在了原作者的脑海中,别人无法获取。只有神和原作者知道。时间久一点后,就只有神知道了。

    强化数据结构的表达

    阅读到此,我们已经非常确定数据的流向是隐含于数据结构之上的,只是数据结构太沉默,缺乏表达。而逻辑控制流又太活跃,喧宾夺主。

    所以,我们要强化数据结构的表达,在数据结构上描述数据的流向,也就是结构之间的关联。

    事实上,当我们把几个独立的数据结构,编排组织在一起成为一个较大的新的结构时,这种关联就已经建立了,只是我们并未对其进行更多的、更详细的描述,关联隐晦其中。我们只是把它当作了数据的存储,然后通过逻辑控制流再对其进行读读写写。

    寻找解决方案

    有两种方案可以更好的描述结构之间的关联。

    注释型方案

    一种是注释型方案,在数据结构上通过注解来解释并描述结构之间数据的流动,可以描述流动的方向性,然后通过预处理,静态编译来校验结构之间关联的正确性。我们需要实现一个静态的编译检查工具。

    依然还是在逻辑控制流中对数据的流向进行控制,只是额外增加了在数据结构定义时的说明,弊端是程序员可以偷懒不进行注解,这样就又退化回了原始的蛮荒。更主要的弊端是,还是采取了以逻辑控制流为主的编码方式,也就保留了前文说过的逻辑控制流的所有缺点,虽然通过注解有了数据流向的地图,但是辅助作用有限,阅读并梳理代码还是有较大难度,治标不治本。

    数据结构化编程

    另一种方案是,以数据结构为主,以逻辑控制流为辅,也就是面向数据结构编程(数据结构化编程),而非面向过程。

    注意,这不是为了提出新概念,也不是为了喊口号,而是为了改变以往的过程式思维方式,转到结构化思维方式上来。意识形态决定思维方式,思维方式决定实际行为。

    这就是为何一直要强调结构的重要性来建立意识形态,从而塑造结构化思维,从而决定实际的代码编写。这个初始的思维转变可能不太习惯,请耐心些,因为我们已经越过了逻辑控制流的层层迷雾,开始直接面对程序的核心“数据结构”,握住了程序的命脉。

    结构化思维 & 结构的规则

    改变根深蒂固的看法

    在结构化思维方式中,我们首先要改变把数据结构仅仅当作数据存储这一根深蒂固的看法。

    或许你会认为这违背了以往的准则,离经叛道,数据结构就应该也仅应该当作逻辑控制的存储区域,这样才有简单性。貌似没错,但实际上整个程序代码变简单了吗?用事实说话就是并没有!唯一保持“简单”的只有数据结构,能不简单吗,就是声明定义了一个变量而已,当然简单了。但为此付出的代价是无比高昂的,仅为了保持变量声明的简单性,而不得不在逻辑控制流中来控制其流向,操纵其行为,通过各种令人绝望的骚操作来让其运动起来,引发绝望的复杂性。

    这就好比,太阳和地球纯粹就是简单的球体而已,是固定静止的,没有地球绕着太阳旋转,而如果要让地球绕着太阳旋转的话,则只能由上帝额外施加一个 神力 来让地球绕着太阳旋转。如果我们的宇宙真是这样的话,我们永远无法弄清楚宇宙天体的运行规律,绝不可能!这直接丢弃了万有引力,而是通过神力来模拟引力的效果,而神力却是我们永远无法触及的,除非我们是神。更恐怖的是,一旦引入这个神力,就会破坏原来世界的平衡(蝴蝶效应),而为了维持平衡,则会引入更多的神力,从而陷入无尽的循环。一个充满神力的世界,是极致复杂的,永远不可能弄清楚。

    而我们却习惯于在程序代码中充当神的角色,用各种神力来实现目的,并艰难的维护着平衡,以至于自己也难以看懂。摘去神力的面具,这其实是幽灵之力,魔鬼之力,地狱之力,想当神的我们,却实际成为了撒旦。

    改变了看法之后,数据结构就不应该是一个静静的数据存储了,数据结构本身一定是有自己的规则。 就像太阳和地球一样,其自身就具有引力的规则,可以自我解释清楚,而不是借助外部的神力。宇宙的规则“物理”的简单之处也就是所有的规则一定是存在于其物质本身的,通过对物质本身的结构和规则进行探索,就能掌握万物的奥秘。

    改变主观视角

    所以我们要在数据结构上定义规则,用其本身的规则来描述结构之间的关联。

    在结构化思维方式中,我们思考的角度应该立足于数据结构上。 这就产生一个非常有意思的思考流程:

    1. 首先告诫自己,这是一个从输入数据转化为输出数据的运动规则,一定抽象出两类字段,即输入字段和输出字段。
    2. 分析需求,找出提供的物料和期望的结果,在结构上定义一个或多个输入字段,和一个或多个输出字段。
    3. 因为要定义规则,规则的最大特点就是可以被动的自动执行(物理规则也是如此;其不同于函数,函数是主动执行的),所以要找到执行的时机。
    4. 考虑到函数执行的时机是当所有入参都传入时,所以很自然的,规则的执行时机,应该是规则所依赖的输入字段都准备好时。
    5. 这里获知一个很有趣的性质,就是规则的执行是不依赖入参的顺序的,也即无需操心字段的赋值顺序,程序员只需要负责把字段塞满就好,如果有规则该执行,那么就会自动执行。
    6. 由此,定义规则就变得简单了,主要分为三部分:
      • 所需要的条件(输入数据),即声明需要哪些输入字段
      • 规则细节(数据转化的过程),即实现数据转化的过程
      • 产生预期输出结果(输出数据),即标明会有哪些输出字段(暂时简化,写此文时,在评估确定较好的通用标明方案)
    7. 发现结构化的一个重要性质,即数据流向是清晰的,易于梳理的,这对可维护性很重要。(达到理清数据流向的目标
    8. 发现结构化的另一个重要性质,即数据结构的复用性很高,因为每个结构都是自洽的。
    9. 对于复杂的需求,可以构建树形数据结构,由子结构的输出结果作为父结构所需要的条件,执行规则,产生属于父结构的输出结果。最终通过这种简单的方式,在最外层生成最终的预期输出结果,交付给用户。
    10. 最终,重新认知到,程序是由简单易懂的数据结构遵循规则运行后,产生预期输出结果(输出字段),而不应该是进入黑盒函数后经过玄学操作返回结果。这是因为,前者的结果是可以预期的,规则是恒定的,可以扁平展开的;而后者的结果是难以预期的,也难以进入梳理运行流程,复杂令人绝望。

    结构化的基本性质和特性

    可以发现结构化的三大基本性质:易于读写、流向清晰、高度复用
    和高级特性:安全、并发。字段会对数据进行完备校验(不仅校验类型,也校验数据值是否符合预期规则),每个字段都是安全的,那么整个程序就会是安全的。由于字段赋值是不要求顺序的,因而可以有更好的并发性。

    定义结构 & 定义规则

    实际编程需要做的只有 2 、6 两步,这也正是结构化思维后数据结构的两个根本构成:字段和规则。在以往只有字段的情况下,新添加了规则这一个概念,整个程序世界变得完全不同了,开始变得清晰明朗了,一直萦绕的迷雾消失不见了,如透明般展示于我们的眼前。就像有了货币规则之后,人类的整个文明世界都变得不同了,社会飞速进步。

    显而易见的,在走完整个思考流程后,最终我们对程序有了简单易懂的全新认知,获益是巨大的。整个程序如树形结构一般清晰的展示于我们眼前,对于我们阅读来讲,基本上只需要关心字段的输入和输出,而不必探究详细的实现,因为字段是如此的清晰易读。每一个子结构都可以使用同样的方式,扁平展开,阅读复杂度是恒定平缓的,而这在过程式编程中是无法想象的,是不可能的,其复杂度如蜗牛一般是螺旋状的,总能把人搞晕,代码量越多,复杂度就越恐怖。而现在我们终于找到了结构化编程,将复杂度变得平缓,有一种打破枷锁的解放感。

    耳听为虚,眼见为实,接下来通过一个示例来体会下结构化编程的简单易懂。(此为演示代码,现在已经有 TypeScript 版可用: https://rainforesters.github.io/rainforest-js

    // 定义结构 // 定义跳跃的信号结构 typedef JumpSignal { jump bool event Event stage Stage } // 定义规则函数 // 这个规则应该解读为:将用户的操作转化为跳跃信号 funcdef JumpSignal ( // 声明待观察的字段(可以有多个) // 一旦所有字段都准备好(被赋值),则触发规则函数 event ) => { if (self.event.type == "tap") { self.jump = true // 为 jump 字段赋值,表示 jump 字段已经准备好 } } funcdef JumpSignal ( stage ) => { self.stage.addEventListener("tap", (e) => { self.event = e }) } typedef Avatar { ... } typedef Player { avatar Avatar jumpSignal JumpSignal } // 这个规则应该解读为:当跳跃信号发生时,将角色变成跳跃状态 funcdef Player ( jumpSignal { // 可以直接观察子结构的字段,而无需关心 jumpSignal 是否为空, // 因为,遵循结构化编程的思维,这里只需要声明待观察的目标字段, // 也就是,我们只期望 jump 准备好, // 这也就间接表明了 jumpSignal 肯定会准备好。 // 这种简单直白的条件声明,是以往过程式编程所无法实现的。 jump } ) => { if (self.jmpSignal.jump) { self.avatar.state = "jump" } } func main() { // 初始化结构实例 // avatar jumpSignal 会自动初始化 const player = typeinit(Player) player.jumpSignal.stage = stage // 将舞台赋值给跳跃信号 } 

    核心是以数据结构为主,在其上定义规则,充分利用规则的被动自动执行。
    整个程序的结构现在是完全可视的了,子结构都层次分明,职责明确,可以快速的弄懂结构的意图,梳理整个程序的架构,理清数据的流向。我们能避免像以往过程式编程那样掉入逻辑漩涡,无法自拔,可以松快的读写代码。

    结构化编程的新写法

    以往的过程式编程的写法是,需要通过逻辑控制流,一步一步小心翼翼的线性编写,同时还要考虑最令人烦恼的分支判断等,需要费很大的力气来编写。

    现在,编程方式开始有变化了,通过结构化编程,我们获得了三个恒定的写法:

    • 定义数据结构
    • 定义规则
    • 为字段赋值(让数据准备好)

    得益于结构的高复用性,所以,最常用的写法就是为字段赋值,这个活很好干。不用引入逻辑控制流,不用操心赋值顺序,只要需要被赋值,那就赋值好了,只要需要准备的都准备好了,系统会聪明的遵循规则自动运行。这就像是在设置配置文件,简单直白,通俗易懂。

    一定要有新认知、新思维

    可能依然有些同学会觉得上述示例晦涩难懂,发现不了结构化编程的好处,说白了就是一时还没有转过弯来,依然用过程式的思维来阅读上述代码,旧思维太固执,太根深蒂固。

    正如前面花大力气强调的,一定要认知到结构的重要性,一定要改变数据结构是数据存储的固执看法,一定要秉承结构化的思维,以数据结构为主,着眼于结构本身去理解规则。 意识形态决定思维方式,思维方式决定实际的阅读和编写行为。

    总之,要扭转过程式思维到结构化思维上来,想通了,就明朗了。

    结构化编程的好处

    使用结构化编程,我们能获得一些前所未有的令人欣喜的好处,如我们可以直接从结构化的三大基本性质(易于读写、流向清晰、高度复用)中获益,也可以从更高级的安全和并发特性中获益。还有就是得益于结构化编程的新写法,我们可以实现更多工具在静态检查阶段或运行阶段,通过可视化的方案来一览整个程序的数据流向,可以知道哪些字段是输入数据;哪些字段是输出数据;哪些字段是被依赖的,又间接依赖哪些字段;哪些字段已经准备好;哪些字段尚未准备好,等等等等。 这在以往的过程式编程中是难以实现的。

    还有一个不得不说的性质:高度复用
    同样得益于结构化编程的新写法,我们统一了结构引用定义的方式,和为字段赋值的方式。
    这就表示,你自己定义的或者他人定义的结构,都能够复用、通用。这也表示了,如果大家开放自己定义的结构给所有人使用,那么随着积累、沉淀,我们写的代码将会越来越少。
    以往,我们受限于组件的不可检索性,只能通过一个项目的文字简介来获知是否对自己适用,而现在得益于结构的完整自描述性(自洽),可以归纳出输入字段的类型和输出字段的类型,构建索引,从而可以提供终端的用户检索。
    同时,由于数据结构代表数据本身,可以方便进行序列化,这就表示,可以抹平不同编程语言之间的差异,可以方便的跨语言执行规则,也易于分布式执行规则。

    另一个不得不说的特性:安全
    毫不客气的讲,绝大多数程序员最容易忽视的就是安全,除了讨厌写注释之外,就是讨厌写安全校验,不喜欢穿衣服就出门。还有一个长久困扰程序员的麻烦是 Debug,除去明显的逻辑错误,大多数 Debug 的过程其实是把异常数据校正的过程,但是追踪问题根源却要耗费九牛二虎之力。
    现在,受益于结构的自洽,我们也可以让类型自洽,对类型进行自我描述,不但要校验输入数据的类型,同时也校验数据的值是否合规,是否符合预期。也即类型是可以有明确语义的,可信的,可以预期的,是安全的。
    比如期望是偶数,那么就定义一个偶数类型,然后作为数据结构的字段类型,那么对任何偶数类型字段的赋值都会自动进行校验,校验的目的就是要确保符合预期,保持确定性。
    每个字段都是符合预期的,那么整个结构就是符合预期的,从而整个程序也是符合预期的,是安全的。整个程序的状态在任何时刻都是确定的,任何时刻都是安全的。

    清晰的数据流向也提高了 可维护性
    现在,整个程序的流向地图不是隐藏于原作者的脑海了,在编写程序的时候就已经开始绘制出来了,这对于后续的维护者来说是天大的福音。
    同时,得益于上面所说的安全,让结构保持在确定的、可以预期的状态,这也能减少 Bug 的产生,降低 Debug 的难度。

    结构化思维带来的编程写法也更有助于 多人协作
    多人协作的开发方式,提高了开发速度,但也增加了整个程序的不确定性和沟通成本。每个人的思维逻辑都不同,所以代码实现也五花八门,对接接口也千奇百怪。再好的开发规范,也只能约束代码的编写行为,却没办法规范统一人们的逻辑实现。不同的对接风格,增加了沟通成本,也无法保证确定性。
    遵循结构化思维,能够将开发人员从以往的主观逻辑实现,拉回到客观的结构规则上来,消除主观差异性,回归到结构本质的输入和输出,令其自洽,易于大家共识。为字段赋值的写法,也简单直白的统一了对接风格,降低沟通成本。并且得益于安全特性,能够更好的消除多人协作带来的不确定性,以更低成本,保证整个程序的安全稳定。

    应用场景

    结构化编程的方式,除了常规的前端、后端,对于运维等也是大有裨益,就像写配置文件。
    由于结构的自洽性,其相当于是物理概念的物质,所以理论上只要结构自身的规则准确,那么可以更容易的对任何场景进行模拟,比如对互相干涉性较强的星系模拟、气候模拟、神经模拟等也非常有潜力。

    总结

    如前所述,此文的意义在于,打破根深蒂固的陈旧过程式编程观念和思维,找到结构化编程的新观念、新思维,站在更好的视角,拥有更广阔的视野,重塑对程序的认知,建立新的意识形态,发挥优势,提高生产力

    现在,让我们的思维解放吧!

    48 条回复    2021-02-11 10:32:34 +08:00
    xxfboy
        1
    xxfboy  
       2021-02-05 09:37:37 +08:00
    nb
    NexTooo
        2
    NexTooo  
       2021-02-05 09:43:32 +08:00   13
    翻了半天没看到营销链接,怅然若失
    是我层次太浅了没看出来吗……
    clown007
        3
    clown007  
       2021-02-05 09:45:36 +08:00
    我直接来看评论了...
    Justin13
        4
    Justin13  
       2021-02-05 09:51:29 +08:00 via Android
    有限状态机?
    thrinity
        5
    thrinity  
       2021-02-05 09:55:19 +08:00
    太深奥了
    lskjdfgl
        6
    lskjdfgl  
       2021-02-05 10:01:39 +08:00
    @NexTooo 可能楼主忘记留公众号或二维码了
    vaporSpace
        7
    vaporSpace  
       2021-02-05 10:02:20 +08:00
    这比喻有意思
    zion03
        8
    zion03  
       2021-02-05 10:06:13 +08:00
    TL;DR
    jheroy
        9
    jheroy  
       2021-02-05 10:08:15 +08:00
    难道不是一直这样写代码的么。过程化编程还是上古时代用 goto 的时候吧。
    reed2020
        10
    reed2020  
       2021-02-05 10:09:20 +08:00
    快,放链接!
    muchan92
        11
    muchan92  
    OP
       2021-02-05 10:14:36 +08:00
    @jheroy 数据结构化编程,不是 `逻辑` 结构化编程
    chinaliuhan
        12
    chinaliuhan  
       2021-02-05 10:19:36 +08:00
    学习了
    jheroy
        13
    jheroy  
       2021-02-05 10:19:45 +08:00
    @muchan92 就是以数据为中心嘛,我觉得你说的这种更像是函数式编程,而不是结构化编程,毕竟函数式编程就是“倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。”
    VoidChen
        14
    VoidChen  
       2021-02-05 10:20:18 +08:00
    盗墓就盗墓,笔记是笔记
    xingshu1990
        15
    xingshu1990  
       2021-02-05 10:22:04 +08:00
    @NexTooo #2
    @lskjdfgl #6
    推广的就是那个 github 项目
    jheroy
        16
    jheroy  
       2021-02-05 10:23:36 +08:00
    而且你说的这几个有点:高度复用,安全,可维护性,多人协作,基本上和函数式编程一模一样。代表语言就是 erlang, 连思维模式要转换一下这个特点都是一样的。
    ming168
        17
    ming168  
       2021-02-05 10:26:54 +08:00
    nb
    jheroy
        18
    jheroy  
       2021-02-05 10:30:04 +08:00
    还有你说的“以数据结构为主,以逻辑控制流为辅”,这叫λ演算,“是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义、函数如何被应用以及递归的形式系统”,这是函数式编程的基础。
    vindurriel
        19
    vindurriel  
       2021-02-05 10:41:18 +08:00 via iPhone
    数据驱动 之前看 d3.js 的代码感觉非常简洁有力
    ianshow15
        20
    ianshow15  
       2021-02-05 10:44:26 +08:00   2
    我以为是说写代码跟去上坟一样呢
    muchan92
        21
    muchan92  
    OP
       2021-02-05 10:46:44 +08:00
    @jheroy 函数式编程根本上是逻辑过程的拆分、封装、演进,粒度不同。
    以逻辑为主的编程方式就像是:Hey, 都精神点,跟着本人的思路认真听讲了,不然走神儿就非得晕了不可。
    以数据结构为主的编程方式就像是:来来来,我带你们看看我们的工厂,这些工厂把原材料进行加工产出成品,形成一个产业链。
    由于数据结构的自洽性,预期的确定性,就像是甲方提出需求由乙方提供结果,程序员将精力注重于可信的、可以预期的输入和输出,而不必按步走完逻辑流程才能知晓结果。同时结构化也能有更好的可视化数据流向。
    han0101
        22
    han0101  
       2021-02-05 10:59:34 +08:00
    千言万语不如一个 demo 来的清晰
    wr516516
        23
    wr516516  
       2021-02-05 11:04:02 +08:00
    以为又是个抱怨帖准备转行的呢
    hgjian
        24
    hgjian  
       2021-02-05 11:09:50 +08:00 via Android
    竟然没有留公众号
    Deeymmm
        25
    Deeymmm  
       2021-02-05 11:14:07 +08:00
    @ianshow15 哈哈哈哈哈哈哈
    ixx
        26
    ixx  
       2021-02-05 11:20:21 +08:00   2
    谁把这粽子放出来的?
    sayid233
        27
    sayid233  
       2021-02-05 11:26:19 +08:00
    我以为是像盗墓笔记一样抄袭鬼吹灯呢
    taowen
        28
    taowen  
       2021-02-05 11:41:35 +08:00   1
    https://lobste.rs/s/2ijdt3/reasoning_about_code_is_scam 所谓的让代码更可读,就是一场骗局
    kejinlu
        29
    kejinlu  
       2021-02-05 11:58:51 +08:00
    关键关键还是专业领域的知识。
    muchan92
        30
    muchan92  
    OP
       2021-02-05 15:12:47 +08:00
    @taowen 原作者的想法很有意思,不过却是唯心主义的表达。
    人类作为会思考的动物,在对于 `概念` 性问题上,由于个体或群体的思维差异,可能会有不同的解读,比如,对于水果的名称,可以用中文 苹果,也可以用英文 Apple 。
    而一旦对于真实的 `物质` 性问题上,却会完美的达成一致,比如,对于水果的数量,没有就是 `0` 或 `零` 或 `zero`,一个就是 `1` 或 `一` 或 `one`。
    同样的,唯物主义在基于自洽、规则恒定的前提下,也不否认多样性,比如,光速是恒定的,但是在不同的传播介质中其速度是并不相同的,但在每种介质中速度都是恒定的,这是规则,这是我们的这个世界的物质规则。
    也依然如此,人类有中文、英文等种种语言,但它们的规则也是恒定的用来沟通。

    所以,过程式代码之所以较难阅读,是因为我们阅读的作者的思维,不同的作者有不同的方式来 `驱动` 数据,总之最终给出一个大家都认可的结果。
    而结构化思维却能将作者拉回到数据结构(物质)本身的规则上来,能够很大程度上达成规则的统一,可能依然有多样性,但也只不过是物质规则的描述并不完全而已。
    taowen
        31
    taowen  
       2021-02-05 15:27:31 +08:00
    @muchan92 看过了你的 git 仓库。你有了解过 vue 的写法吗? https://cn.vuejs.org/v2/guide/computed.html 感觉两者是比较类似的
    Redbeanw
        32
    Redbeanw  
       2021-02-05 15:35:40 +08:00
    可以说说您新出版的书吗?
    muchan92
        33
    muchan92  
    OP
       2021-02-05 15:46:27 +08:00
    @taowen 你所说的类似,是响应式上看起来相似,当数据发生变化时触发钩子。

    单纯从代码层面上讲,两者的两个主要区别是:
    * vue 3 是在 effect 里隐式追踪依赖,rainforest-js 是要求明确指定依赖。
    * vue 3 里的子结构依赖需要由程序员自己保证父结构不为空,rainforest-js 里是仅声明需要的目标依赖,无需关心父结构是否为空(遵循结构化的思维)。

    从思维方式和代码编写层面上讲,vue 3 是过程式编程,由作者的逻辑主动驱动数据,rainforest-js 是定义明确的预期规则,然后将该准备的数据都准备好,规则会被动的自动执行,一切是可以预期的。
    muchan92
        34
    muchan92  
    OP
       2021-02-05 15:51:46 +08:00
    @taowen 同时,明确的声明预期条件,也就明确的描述了数据的流向,可以让其他人清晰的理清数据流向。
    wpf375516041
        35
    wpf375516041  
       2021-02-05 15:52:05 +08:00
    寻龙分金看缠山
    一重缠是一重关
    关门如有八重险
    不出阴阳八卦形

    不会寻龙诀,你玩个 der 啊
    taowen
        36
    taowen  
       2021-02-05 16:05:11 +08:00
    @Redbeanw 什么书? https://autonomy.design/ 吗?
    taowen
        37
    taowen  
       2021-02-05 16:09:52 +08:00
    @muchan92 既然你使用了数据流向这个词,那应该了解一下 https://jpaulm.github.io/fbp/ 。这种写法都是把尽可能多的代码写成时序无关的形式(以某种依赖订阅的方式来描述因果关系)。从而让尽可能少的代码是 temporal coupled 的。做到极致就是所有的代码都是 side-effect free 的,也就是 http://curtclifton.net/papers/MoseleyMarks06a.pdf 中描述的 functional relational programming 。实际上也就是 event sourcing 的写法。
    TargaryenChen
        38
    TargaryenChen  
       2021-02-05 16:22:42 +08:00
    TL;DR
    ntest
        39
    ntest  
       2021-02-05 20:32:27 +08:00
    Exin
        40
    Exin  
       2021-02-06 23:21:00 +08:00
    我对大部分内容都非常有共鸣,我也想写一写这方面的思考但是一直表达得不好,楼主写得比我清楚多了
    但我觉得这一层次的理解(至少对我的理解而言),离原理还是差得很远,不知道何时能够参透真相
    lap510200
        41
    lap510200  
       2021-02-07 09:20:18 +08:00
    你一本正经的写 我没有一本正经的看完 也没有收获什么 是我的层次太低了吗
    muchan92
        42
    muchan92  
    OP
       2021-02-07 15:35:51 +08:00
    @Exin 最重要的可能是我已经在探索该观念的历程中(比文中所述更详尽,更原始),已经渐渐熟悉并转变自己的思维了,所以表达的时候是以一个潜意识已经接受者的角度去落笔,对于这个观念几乎没有任何思想准备的朋友们而言,思维跨度太大,我较难表述,大家也较难接受。

    所以我觉得,大家可以就从没有思想准备的视角下,提出见解来。
    我们可以多多探讨,多多交流,把你的想法都表述出来,大家也把想法都表述出来,看看哪个思考节点较难理解,哪个节点会有疑惑,哪个节点会有不同见解,等等,这样也有助于我对错误的更正,也有助于大家对该观念的认知和了解,在互相探讨的过程中,我们能够更清晰明白的接受或发现更精彩的新想法。
    muchan92
        43
    muchan92  
    OP
       2021-02-07 15:37:46 +08:00
    @lap510200 有什么具体的想法,直接表述出来就好,像现在这么说,我实在没法回答。
    有什么想法,随意提问,我们之所以在这,不就是为了讨论,探讨么,在探讨的过程中,我们才能更好的一步步消去迷雾,获得明白,探讨过程本身也是很有意思、很有意义的不是么。
    frandy
        44
    frandy  
       2021-02-10 11:57:44 +08:00
    恕我愚钝,怎么越看越觉得是面向对象的理念。面向对象里面,字段和规则,不就是对象里面的属性和方法吗。只是目前我们开发普遍喜欢让对象只有字段,而让规则分离到了服务层面,所以服务层太厚。所以 DDD 出现了。我觉得大佬你的观点是不是有些 DDD 的味道呢。
    muzuiget
        45
    muzuiget  
       2021-02-10 12:41:53 +08:00
    直奔评论+1,居然没有公众号。
    muchan92
        46
    muchan92  
    OP
       2021-02-10 14:26:36 +08:00
    @frandy 如果把**规则**误解为是对象的方法,那么就会产生面向对象的误解。
    我们先不要把视角放在结构规则与对象方法的定义写法上面,忽略写法的相似性。
    我们先把视角放在两者最终是如何使用的,看一下显著差别。

    面向对象编程中,最终的调用是由程序员**主动**调用一个或多个对象的方法(或一系列方法),由程序员自己一步步推动 input data 经过一系列逻辑或方法,最终得到 output data 。

    数据结构化编程中,最终会是一个像配置文件的入口,只需要把待赋值的 input 字段赋值好就行,不用关心顺序,不用关心层级,只要所有预期的 input 字段都准备好了,那么程序会**被动**自动执行,产生 output data 。

    这能看出,面向对象或面向过程的编程,都是需要用力在 data 背后主动推动其进行流动,数据流向只能通过深入阅读细节源码才能知晓,这也是难以维护的重要原因。
    数据结构化编程,则并不用费力气去推动 data,而是变成了仅提供 data,只要该有的都有了,那么就会自动执行。其数据流向是可以通过数据结构来进行阅读,并不需要深入逻辑源码;并且正如文中 `结构化编程的好处` 提到的,可以用工具,以可视化的图形方式,展示出整个程序的所有结构之间的流向图等等;额外的,如果编写有误,也可以直接显示出来等等。

    还是用文中的举例,结构化编程讲究的是**自洽**,就像是为太阳、地球定义引力的规则,然后把它们随便扔到任何空间中,它们总会按照引力的规则开始旋转。而过程式编程就需要由神出手,精确摆好太阳和地球的位置,然后再用手费力的在后面推动其进行旋转。

    若你细品的话,在规则的定义方面,有些 DDD 的味道。规则是对结构的自洽描述,如:引力。
    zxyangyu
        47
    zxyangyu  
       2021-02-10 19:11:18 +08:00
    赋值和执行绑定在一起真的没问题吗, 就像你说的引力, 如果你要在太阳系中间增加一个新行星, 是否会打乱整个太阳系的运行? 我觉着函数式编程已经包含了你所说的巨大多数优点, 而且代价更小.
    muchan92
        48
    muchan92  
    OP
       2021-02-11 10:32:34 +08:00
    @zxyangyu 啊哈,果然提到 `三体` 问题了,如果是的话,则用什么都不咋好使,如果不算作 `三体`,那么就是普通两者间的引力。

    关于函数式编程前排提及过,从根本上是过程式编程,是手动驱动数据,另外由于异步或副作用的存在,往往难以写出纯函数。对于纯函数,是具有函数式编程优点的,但是并不总能实现。

    另外对于有些朋友解读为数据驱动,可能那样认知成本更低,但是不建议那么认为,因为容易忽略掉结构化的层级和关联,还有一个显著的区分特点为:是主动驱动数据,还是根据数据的准备情况自动执行。这也用来区分是过程式编程还是数据结构化编程;也是影响程序读写难度的核心。

    对于过程式编程,由于逻辑过程的本质复杂,没有好的手段可以大幅降低开发难度。
    数据结构化编程,无论是从思维上,还是写法上,又或是生态工具上,都有潜力大幅降低开发难度,并且还有许多潜力等待发现。
    以文中的 `安全` 部分举例(也可以参见 github ),任何数据流动都会进行完备校验,而不必像在过程式中,每次都得自己手动进行校验(甚至大多人都不校验)等等。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     877 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 19:51 PVG 03:51 LAX 12:51 JFK 15:51
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86