V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
muchan92
V2EX  ›  程序员

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

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

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

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

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

    分析代码的特点

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

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

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

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

    什么是数据流向?

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

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

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

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

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

    结构是“神”的语言

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

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

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

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

    分析如何理清数据流向

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

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

    这种误解是如何产生的?

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

    强化数据结构的表达

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

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

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

    寻找解决方案

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

    注释型方案

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

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

    数据结构化编程

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

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

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

    结构化思维 & 结构的规则

    改变根深蒂固的看法

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

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

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

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

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

    改变主观视角

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

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

    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.jumpSignal.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   ·   实用小工具   ·   5402 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 07:12 · PVG 15:12 · LAX 23:12 · JFK 02:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.