为什么说写代码如盗墓笔记呢,是因为写代码确实有一些特点和盗墓笔记很像,比如坑比较多,也都处于暗处,没有在明面上,在进入之前是无法知晓整个结构的,只有亲身深入其中,详细探索完每一个地方之后才能知晓。
但是这个探索过程是非常艰难的,因为一切都是未知的,也没有任何导航可用,只能两眼抹黑的往前瞎拱,自然免不了各种迷路,各种碰壁,各种掉坑。
这种艰难的状况经历了多了之后,我们就应该思考了,是什么原因导致了这种状况?那么又有没有方法可以解决这种状况吗?
我们重新审视分析下程序代码的特点,只有了解了这些特点,才能探究出原因。
程序代码主要是由数据变量和逻辑控制流构成的,其中逻辑控制流占据了绝大部分。而逻辑控制流包含了许多分支,就像本来是一条道路,却分叉出了许多支路,选择一条支路,走着走着又继续分叉出更多的支路出来,密密麻麻,乱乱糟糟的,这就导致了这些道路非常难走。并且更令人抓狂的是,这些道路没有导航,没有地图,没有路标,非得抹黑走到路的尽头才能知道尽头到底是什么。
数据变量基本上有两个职能,一个是用来标记逻辑控制流的运行状态;另一个就是用来组成数据结构。而实际上离散的数据变量也算是数据结构的一种,只是不同于经典数据结构,不太明显而已,容易让人误认为其仅是一个离散的变量,而忽略其影响并左右逻辑控制流的结构性的意义。
所以整体看来就是,数据变量和逻辑控制流是互相关联互相影响的。
而从分析过程中也可以知道,程序代码的特点就是分支散碎,藏于尽头,难以获知。导致这一特点的原因也是逻辑控制流的复杂性、散碎、藏于尽头。也即,程序代码的这些不好的特点都是由逻辑控制流体现的。
那么逻辑控制流能避免这些缺点吗?答案是不能。因为这是由于逻辑控制流的根本复杂性和本质复杂性所决定的。逻辑控制流说白了就是平常说的过程,所以也是无法跳过的,因为你不能跳过过程而直接获得结果。
所以从这里看来,程序代码的这些缺点也依然存在,无法避免。
不过有一点非常值得思考,逻辑控制流的目的是什么?是进行数据转换,也就是进行数据结构的转换,也就是描述并控制数据的流向。
这也是程序的本质:从输入数据结构,来获取输出数据结构。
所以,写逻辑控制流的意义不是纯粹为了写逻辑控制流,而是为了进行数据结构的转换,描述并控制数据的流向。因此,我们可以获得一个非常重要的有用信息,只要能够理清数据的流向,就能显著降低阅读代码的难度。
我们阅读代码的目的其实也就是为了弄清楚数据的流向。
那么我们就把重心转移到理清数据的流向上来,分析下如何才能更好的理清数据的流向。
因为逻辑控制流的本质复杂性,所以我们无法通过逻辑控制流来清晰的理清数据的流向,这也正是我们一直以来读写代码所采用的方式,事实证明,这不是好的方式,数据流向散落在逻辑控制流的各个地方,根本没有好的办法可以串联起来,形成一个有导航意义的地图。
前文说过,程序代码是由数据变量和逻辑控制流构成的,也提到过,数据变量和逻辑控制流是互相关联互相影响的,并且数据流向自然也离不开数据变量。既然没有好的办法通过逻辑控制流来理清数据流向,那么就需要思考下在数据变量上是否有好的办法。
数据变量是什么?前文解释过,数据变量就是数据结构。
请着重注意 结构 这个概念,结构表示了层级和关联,而关联就是流向,我们正是想理清流向。所以在数据结构身上,我们看到了理清数据流向的希望。
在开始分析如何通过数据结构来理清数据流向之前,我觉得有必要,也非常有必要重新强化对结构的认知。因为有太多的小伙伴们对结构没有足够的重视,不以为然,莫不关心,没有认知到结构的重要性以及不可或缺性以及根本本质性。
我们眼前的一切,周围的一切,手机、电脑、衣食住行、山川大海、以及你我,都是由结构构成的,更确切的讲,这一切就是结构。我们第一次认知这个世界就是从结构开始,第一次睁眼看到父母,第一次拆解玩具,第一次感受花草。人类学习结构,利用结构,组成各种各样的新的结构、新的工具来帮助我们生活。人类研究周围的各种物体,以及日月星辰、宇宙天体,其实就是在探索它们的结构,和结构之间的关联。只有知晓了它们的结构与之间的关联,我们才能组装更高级的结构来生产工具,才能进一步知晓周围万物的原理和世界的运行规律。
我们都知道我们身处的宏观世界是三维的立体世界,这个世界中的任何物体都有其形状,有其结构,形状就是结构,没有结构将无法存在于这个世界中。即使加入看不见摸不着的时间维度组成四维的时空世界,结构依然存在于任何一个维度之中。我们都知道万有引力,其中引力并不是虚无飘渺的能量,而是质量在时空中引起的时空扭曲,正是因为时空可以被扭曲,所以任何一个维度都是有结构的,人类一直在探索其中的结构和关联。
在微观世界中,物质是由分子构成的,人类对分子的结构进行探索发现了原子,对原子的结构进行探索发现了原子核,继续不断对结构进行探索发现了夸克。反过来讲就是,各种更小的粒子结构通过 关联 组合成较大的粒子结构,层层组合最终构成了宏观世界。化学的本质是物理的粒子运动,粒子运动其实是结构之间的关联,人类的情感、想法等都是细胞间的化学信号,而化学信号又是物理的粒子运动,本质又是结构之间的关联,所以我们人类的一切行为,生物的一切行为都是结构之间的关联。
所以,结构是“神”的语言,用结构和结构之间的关联来描述整个世界。
数据结构之于程序代码也同样如此,其是程序的根本,数据结构与之间的关联描述了整个程序世界,逻辑控制流依存于数据结构,若离开其,则程序将崩塌不复存在。
现在我们开始分析如何通过数据结构来理清数据流向。
由前文可知,数据的流向是由逻辑控制流来控制的,但是逻辑控制流本身就是复杂的,现在我们换个说法,数据的流向是由逻辑控制流来驱动的,逻辑控制流只是通过一些条件、规则来驱动数据进行流动,其流向并不是在写逻辑控制流的过程中而突然打通的,而是在写逻辑控制流之前,在数据结构定义的时候,就已经开始设计构思其流向了。只是几乎我们每一个程序员都忽略了这一点,我们错误的以为数据流向是存在于逻辑控制流之中的。
首先,程序的原作者在定义数据变量也即数据结构的时候,就已经在脑中设计构思其流向了,但他并没有在该数据结构上做说明来描述数据的流向。然后,他认为数据的流向应该用逻辑控制流来描述就可以,就够用了,所以就零零散散的用代码在逻辑控制流中进行描述。这就导致了别人不得不完整阅读逻辑控制流中的散碎的、反人类的代码来弄清楚数据的流向,原作者留给后来人的仅有这一堆蜿蜒曲折,支离破碎的代码,而清晰的导航地图只留在了原作者的脑海中,别人无法获取。只有神和原作者知道。时间久一点后,就只有神知道了。
阅读到此,我们已经非常确定数据的流向是隐含于数据结构之上的,只是数据结构太沉默,缺乏表达。而逻辑控制流又太活跃,喧宾夺主。
所以,我们要强化数据结构的表达,在数据结构上描述数据的流向,也就是结构之间的关联。
事实上,当我们把几个独立的数据结构,编排组织在一起成为一个较大的新的结构时,这种关联就已经建立了,只是我们并未对其进行更多的、更详细的描述,关联隐晦其中。我们只是把它当作了数据的存储,然后通过逻辑控制流再对其进行读读写写。
有两种方案可以更好的描述结构之间的关联。
一种是注释型方案,在数据结构上通过注解来解释并描述结构之间数据的流动,可以描述流动的方向性,然后通过预处理,静态编译来校验结构之间关联的正确性。我们需要实现一个静态的编译检查工具。
依然还是在逻辑控制流中对数据的流向进行控制,只是额外增加了在数据结构定义时的说明,弊端是程序员可以偷懒不进行注解,这样就又退化回了原始的蛮荒。更主要的弊端是,还是采取了以逻辑控制流为主的编码方式,也就保留了前文说过的逻辑控制流的所有缺点,虽然通过注解有了数据流向的地图,但是辅助作用有限,阅读并梳理代码还是有较大难度,治标不治本。
另一种方案是,以数据结构为主,以逻辑控制流为辅,也就是面向数据结构编程(数据结构化编程),而非面向过程。
注意,这不是为了提出新概念,也不是为了喊口号,而是为了改变以往的过程式思维方式,转到结构化思维方式上来。意识形态决定思维方式,思维方式决定实际行为。
这就是为何一直要强调结构的重要性来建立意识形态,从而塑造结构化思维,从而决定实际的代码编写。这个初始的思维转变可能不太习惯,请耐心些,因为我们已经越过了逻辑控制流的层层迷雾,开始直接面对程序的核心“数据结构”,握住了程序的命脉。
在结构化思维方式中,我们首先要改变把数据结构仅仅当作数据存储这一根深蒂固的看法。
或许你会认为这违背了以往的准则,离经叛道,数据结构就应该也仅应该当作逻辑控制的存储区域,这样才有简单性。貌似没错,但实际上整个程序代码变简单了吗?用事实说话就是并没有!唯一保持“简单”的只有数据结构,能不简单吗,就是声明定义了一个变量而已,当然简单了。但为此付出的代价是无比高昂的,仅为了保持变量声明的简单性,而不得不在逻辑控制流中来控制其流向,操纵其行为,通过各种令人绝望的骚操作来让其运动起来,引发绝望的复杂性。
这就好比,太阳和地球纯粹就是简单的球体而已,是固定静止的,没有地球绕着太阳旋转,而如果要让地球绕着太阳旋转的话,则只能由上帝额外施加一个 神力 来让地球绕着太阳旋转。如果我们的宇宙真是这样的话,我们永远无法弄清楚宇宙天体的运行规律,绝不可能!这直接丢弃了万有引力,而是通过神力来模拟引力的效果,而神力却是我们永远无法触及的,除非我们是神。更恐怖的是,一旦引入这个神力,就会破坏原来世界的平衡(蝴蝶效应),而为了维持平衡,则会引入更多的神力,从而陷入无尽的循环。一个充满神力的世界,是极致复杂的,永远不可能弄清楚。
而我们却习惯于在程序代码中充当神的角色,用各种神力来实现目的,并艰难的维护着平衡,以至于自己也难以看懂。摘去神力的面具,这其实是幽灵之力,魔鬼之力,地狱之力,想当神的我们,却实际成为了撒旦。
改变了看法之后,数据结构就不应该是一个静静的数据存储了,数据结构本身一定是有自己的规则。 就像太阳和地球一样,其自身就具有引力的规则,可以自我解释清楚,而不是借助外部的神力。宇宙的规则“物理”的简单之处也就是所有的规则一定是存在于其物质本身的,通过对物质本身的结构和规则进行探索,就能掌握万物的奥秘。
所以我们要在数据结构上定义规则,用其本身的规则来描述结构之间的关联。
在结构化思维方式中,我们思考的角度应该立足于数据结构上。 这就产生一个非常有意思的思考流程:
可以发现结构化的三大基本性质:易于读写、流向清晰、高度复用。
和高级特性:安全、并发。字段会对数据进行完备校验(不仅校验类型,也校验数据值是否符合预期规则),每个字段都是安全的,那么整个程序就会是安全的。由于字段赋值是不要求顺序的,因而可以有更好的并发性。
实际编程需要做的只有 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 的难度。
结构化思维带来的编程写法也更有助于 多人协作。
多人协作的开发方式,提高了开发速度,但也增加了整个程序的不确定性和沟通成本。每个人的思维逻辑都不同,所以代码实现也五花八门,对接接口也千奇百怪。再好的开发规范,也只能约束代码的编写行为,却没办法规范统一人们的逻辑实现。不同的对接风格,增加了沟通成本,也无法保证确定性。
遵循结构化思维,能够将开发人员从以往的主观逻辑实现,拉回到客观的结构规则上来,消除主观差异性,回归到结构本质的输入和输出,令其自洽,易于大家共识。为字段赋值的写法,也简单直白的统一了对接风格,降低沟通成本。并且得益于安全特性,能够更好的消除多人协作带来的不确定性,以更低成本,保证整个程序的安全稳定。
结构化编程的方式,除了常规的前端、后端,对于运维等也是大有裨益,就像写配置文件。
由于结构的自洽性,其相当于是物理概念的物质,所以理论上只要结构自身的规则准确,那么可以更容易的对任何场景进行模拟,比如对互相干涉性较强的星系模拟、气候模拟、神经模拟等也非常有潜力。
如前所述,此文的意义在于,打破根深蒂固的陈旧过程式编程观念和思维,找到结构化编程的新观念、新思维,站在更好的视角,拥有更广阔的视野,重塑对程序的认知,建立新的意识形态,发挥优势,提高生产力。
1
xxfboy 2021-02-05 09:37:37 +08:00
nb
|
2
NexTooo 2021-02-05 09:43:32 +08:00 13
翻了半天没看到营销链接,怅然若失
是我层次太浅了没看出来吗…… |
3
clown007 2021-02-05 09:45:36 +08:00
我直接来看评论了...
|
4
Justin13 2021-02-05 09:51:29 +08:00 via Android
有限状态机?
|
5
thrinity 2021-02-05 09:55:19 +08:00
太深奥了
|
7
vaporSpace 2021-02-05 10:02:20 +08:00
这比喻有意思
|
8
zion03 2021-02-05 10:06:13 +08:00
TL;DR
|
9
jheroy 2021-02-05 10:08:15 +08:00
难道不是一直这样写代码的么。过程化编程还是上古时代用 goto 的时候吧。
|
10
reed2020 2021-02-05 10:09:20 +08:00
快,放链接!
|
12
chinaliuhan 2021-02-05 10:19:36 +08:00
学习了
|
13
jheroy 2021-02-05 10:19:45 +08:00
@muchan92 就是以数据为中心嘛,我觉得你说的这种更像是函数式编程,而不是结构化编程,毕竟函数式编程就是“倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。”
|
14
VoidChen 2021-02-05 10:20:18 +08:00
盗墓就盗墓,笔记是笔记
|
15
xingshu1990 2021-02-05 10:22:04 +08:00
|
16
jheroy 2021-02-05 10:23:36 +08:00
而且你说的这几个有点:高度复用,安全,可维护性,多人协作,基本上和函数式编程一模一样。代表语言就是 erlang, 连思维模式要转换一下这个特点都是一样的。
|
17
ming168 2021-02-05 10:26:54 +08:00
nb
|
18
jheroy 2021-02-05 10:30:04 +08:00
还有你说的“以数据结构为主,以逻辑控制流为辅”,这叫λ演算,“是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义、函数如何被应用以及递归的形式系统”,这是函数式编程的基础。
|
19
vindurriel 2021-02-05 10:41:18 +08:00 via iPhone
数据驱动 之前看 d3.js 的代码感觉非常简洁有力
|
20
ianshow15 2021-02-05 10:44:26 +08:00 2
我以为是说写代码跟去上坟一样呢
|
21
muchan92 OP @jheroy 函数式编程根本上是逻辑过程的拆分、封装、演进,粒度不同。
以逻辑为主的编程方式就像是:Hey, 都精神点,跟着本人的思路认真听讲了,不然走神儿就非得晕了不可。 以数据结构为主的编程方式就像是:来来来,我带你们看看我们的工厂,这些工厂把原材料进行加工产出成品,形成一个产业链。 由于数据结构的自洽性,预期的确定性,就像是甲方提出需求由乙方提供结果,程序员将精力注重于可信的、可以预期的输入和输出,而不必按步走完逻辑流程才能知晓结果。同时结构化也能有更好的可视化数据流向。 |
22
han0101 2021-02-05 10:59:34 +08:00
千言万语不如一个 demo 来的清晰
|
23
wr516516 2021-02-05 11:04:02 +08:00
以为又是个抱怨帖准备转行的呢
|
24
hgjian 2021-02-05 11:09:50 +08:00 via Android
竟然没有留公众号
|
26
ixx 2021-02-05 11:20:21 +08:00 2
谁把这粽子放出来的?
|
27
sayid233 2021-02-05 11:26:19 +08:00
我以为是像盗墓笔记一样抄袭鬼吹灯呢
|
28
taowen 2021-02-05 11:41:35 +08:00 1
https://lobste.rs/s/2ijdt3/reasoning_about_code_is_scam 所谓的让代码更可读,就是一场骗局
|
29
kejinlu 2021-02-05 11:58:51 +08:00
关键关键还是专业领域的知识。
|
30
muchan92 OP @taowen 原作者的想法很有意思,不过却是唯心主义的表达。
人类作为会思考的动物,在对于 `概念` 性问题上,由于个体或群体的思维差异,可能会有不同的解读,比如,对于水果的名称,可以用中文 苹果,也可以用英文 Apple 。 而一旦对于真实的 `物质` 性问题上,却会完美的达成一致,比如,对于水果的数量,没有就是 `0` 或 `零` 或 `zero`,一个就是 `1` 或 `一` 或 `one`。 同样的,唯物主义在基于自洽、规则恒定的前提下,也不否认多样性,比如,光速是恒定的,但是在不同的传播介质中其速度是并不相同的,但在每种介质中速度都是恒定的,这是规则,这是我们的这个世界的物质规则。 也依然如此,人类有中文、英文等种种语言,但它们的规则也是恒定的——用来沟通。 所以,过程式代码之所以较难阅读,是因为我们阅读的作者的思维,不同的作者有不同的方式来 `驱动` 数据,总之最终给出一个大家都认可的结果。 而结构化思维却能将作者拉回到数据结构(物质)本身的规则上来,能够很大程度上达成规则的统一,可能依然有多样性,但也只不过是物质规则的描述并不完全而已。 |
31
taowen 2021-02-05 15:27:31 +08:00
@muchan92 看过了你的 git 仓库。你有了解过 vue 的写法吗? https://cn.vuejs.org/v2/guide/computed.html 感觉两者是比较类似的
|
32
Redbeanw 2021-02-05 15:35:40 +08:00
可以说说您新出版的书吗?
|
33
muchan92 OP @taowen 你所说的类似,是响应式上看起来相似,当数据发生变化时触发钩子。
单纯从代码层面上讲,两者的两个主要区别是: * vue 3 是在 effect 里隐式追踪依赖,rainforest-js 是要求明确指定依赖。 * vue 3 里的子结构依赖需要由程序员自己保证父结构不为空,rainforest-js 里是仅声明需要的目标依赖,无需关心父结构是否为空(遵循结构化的思维)。 从思维方式和代码编写层面上讲,vue 3 是过程式编程,由作者的逻辑主动驱动数据,rainforest-js 是定义明确的预期规则,然后将该准备的数据都准备好,规则会被动的自动执行,一切是可以预期的。 |
35
wpf375516041 2021-02-05 15:52:05 +08:00
寻龙分金看缠山
一重缠是一重关 关门如有八重险 不出阴阳八卦形 不会寻龙诀,你玩个 der 啊 |
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 的写法。
|
38
TargaryenChen 2021-02-05 16:22:42 +08:00
TL;DR
|
39
ntest 2021-02-05 20:32:27 +08:00
推荐 clojure,面向数据编程. https://www.manning.com/books/data-oriented-programming
|
40
Exin 2021-02-06 23:21:00 +08:00
我对大部分内容都非常有共鸣,我也想写一写这方面的思考但是一直表达得不好,楼主写得比我清楚多了
但我觉得这一层次的理解(至少对我的理解而言),离原理还是差得很远,不知道何时能够参透真相 |
41
lap510200 2021-02-07 09:20:18 +08:00
你一本正经的写 我没有一本正经的看完 也没有收获什么 是我的层次太低了吗
|
42
muchan92 OP @Exin 最重要的可能是我已经在探索该观念的历程中(比文中所述更详尽,更原始),已经渐渐熟悉并转变自己的思维了,所以表达的时候是以一个潜意识已经接受者的角度去落笔,对于这个观念几乎没有任何思想准备的朋友们而言,思维跨度太大,我较难表述,大家也较难接受。
所以我觉得,大家可以就从没有思想准备的视角下,提出见解来。 我们可以多多探讨,多多交流,把你的想法都表述出来,大家也把想法都表述出来,看看哪个思考节点较难理解,哪个节点会有疑惑,哪个节点会有不同见解,等等,这样也有助于我对错误的更正,也有助于大家对该观念的认知和了解,在互相探讨的过程中,我们能够更清晰明白的接受或发现更精彩的新想法。 |
43
muchan92 OP @lap510200 有什么具体的想法,直接表述出来就好,像现在这么说,我实在没法回答。
有什么想法,随意提问,我们之所以在这,不就是为了讨论,探讨么,在探讨的过程中,我们才能更好的一步步消去迷雾,获得明白,探讨过程本身也是很有意思、很有意义的不是么。 |
44
frandy 2021-02-10 11:57:44 +08:00
恕我愚钝,怎么越看越觉得是面向对象的理念。面向对象里面,字段和规则,不就是对象里面的属性和方法吗。只是目前我们开发普遍喜欢让对象只有字段,而让规则分离到了服务层面,所以服务层太厚。所以 DDD 出现了。我觉得大佬你的观点是不是有些 DDD 的味道呢。
|
45
muzuiget 2021-02-10 12:41:53 +08:00
直奔评论+1,居然没有公众号。
|
46
muchan92 OP @frandy 如果把**规则**误解为是对象的方法,那么就会产生面向对象的误解。
我们先不要把视角放在结构规则与对象方法的定义写法上面,忽略写法的相似性。 我们先把视角放在两者最终是如何使用的,看一下显著差别。 面向对象编程中,最终的调用是由程序员**主动**调用一个或多个对象的方法(或一系列方法),由程序员自己一步步推动 input data 经过一系列逻辑或方法,最终得到 output data 。 数据结构化编程中,最终会是一个像配置文件的入口,只需要把待赋值的 input 字段赋值好就行,不用关心顺序,不用关心层级,只要所有预期的 input 字段都准备好了,那么程序会**被动**自动执行,产生 output data 。 这能看出,面向对象或面向过程的编程,都是需要用力在 data 背后主动推动其进行流动,数据流向只能通过深入阅读细节源码才能知晓,这也是难以维护的重要原因。 数据结构化编程,则并不用费力气去推动 data,而是变成了仅提供 data,只要该有的都有了,那么就会自动执行。其数据流向是可以通过数据结构来进行阅读,并不需要深入逻辑源码;并且正如文中 `结构化编程的好处` 提到的,可以用工具,以可视化的图形方式,展示出整个程序的所有结构之间的流向图等等;额外的,如果编写有误,也可以直接显示出来等等。 还是用文中的举例,结构化编程讲究的是**自洽**,就像是为太阳、地球定义引力的规则,然后把它们随便扔到任何空间中,它们总会按照引力的规则开始旋转。而过程式编程就需要由神出手,精确摆好太阳和地球的位置,然后再用手费力的在后面推动其进行旋转。 若你细品的话,在规则的定义方面,有些 DDD 的味道。规则是对结构的自洽描述,如:引力。 |
47
zxyangyu 2021-02-10 19:11:18 +08:00
赋值和执行绑定在一起真的没问题吗, 就像你说的引力, 如果你要在太阳系中间增加一个新行星, 是否会打乱整个太阳系的运行? 我觉着函数式编程已经包含了你所说的巨大多数优点, 而且代价更小.
|
48
muchan92 OP @zxyangyu 啊哈,果然提到 `三体` 问题了,如果是的话,则用什么都不咋好使,如果不算作 `三体`,那么就是普通两者间的引力。
关于函数式编程前排提及过,从根本上是过程式编程,是手动驱动数据,另外由于异步或副作用的存在,往往难以写出纯函数。对于纯函数,是具有函数式编程优点的,但是并不总能实现。 另外对于有些朋友解读为数据驱动,可能那样认知成本更低,但是不建议那么认为,因为容易忽略掉结构化的层级和关联,还有一个显著的区分特点为:是主动驱动数据,还是根据数据的准备情况自动执行。这也用来区分是过程式编程还是数据结构化编程;也是影响程序读写难度的核心。 对于过程式编程,由于逻辑过程的本质复杂,没有好的手段可以大幅降低开发难度。 数据结构化编程,无论是从思维上,还是写法上,又或是生态工具上,都有潜力大幅降低开发难度,并且还有许多潜力等待发现。 以文中的 `安全` 部分举例(也可以参见 github ),任何数据流动都会进行完备校验,而不必像在过程式中,每次都得自己手动进行校验(甚至大多人都不校验)等等。 |