关于编程思想:编程和英语数学到底什么关系

前景总是有人说学习编程须要英语好,须要数学好,说这些好的人编程就会很容易,就会很快学会,然而,一些英语和数学不是很优异的人,他们却仍然能很好的学会编程,那么,到底编程和英语和数学到底什么关系呢,就着这个问题,对于其外部存在的问题和分割,这里进行了一次大胆的猜测假如和论证,心愿对于您扩大思维,思考这类问题有着新的前途和办法根据.个别认为数学和英语好对于编程无利的思维模式英语跟计算机关系狭义上的意识 代码都是英文的,英语好,便于学习.遇到的问题是英文的,英语好,便于查找问题的本源和答案便于官网英语的浏览,英语好,便于浏览本博主钻研及独到观点 编程学习上的专有名词记忆跟英语的学习办法和思维有着殊途同归之妙,比方英语须要大量的记忆,记忆名词和动词等词,而后,依据学习的词汇去看文章,计算机中存在大量的术语,计算机术语跟英语单词的名词一样,是一种独有的命名形式,学习计算机术语,要像学习英语的名词一样,有着记忆,在探讨问题的时候,专业人士也会采纳术语的交换模式,所以,术语的积攒和记忆,了解学习都是跟英语的记忆学习模式一样的.学习的模式跟英语是统一的,英语学习须要学习单词,语法,而后,造句,成文章,成书,这样的流程,计算机学习相似,学术语(单词),编程语言的格局语法(英语语法),函数体或者办法(造句),我的项目(就是作品).须要长时间,须要与日俱增,计算机常识博大,博大精深,须要每天都学习一点,重复学习,重复记忆,不是一天就能学的什么都会,提高也跟英语一样,不是一天英语就能全学会,是要长时间,迟缓进步的!说成白话文,很多计算机的我的项目,小例子理论是在做着跟英语翻译一样的工作,只不过,英语是将汉语翻译成英文,或者英文翻译成汉语(对于中国人来讲),而计算机的编程语音,是将自然语言,翻译成计算机语言,或者讲计算机语言转化为自然语言.数学跟计算机关系狭义上讲 数学好,能够更好的了解形象数学好,对于算法的帮忙比拟大本博主钻研及独到观点 计算机里很多概念是数学根底的名词在计算机换了名字,或者用计算机进行了新的定义,比方数这个概念在计算机里就会相似为数据类型,数据类型还是跟中学的数的概念存在类似的中央!很多计算形式也是根底数学的计算形式很多计算方法也是数学的解题办法和思路须要跟做数学题一样,变动的状况比拟多,要重复练习,反复研究,重复思考综上所述,计算机是一门文理综合的科目,须要理科记忆力记住概念,了解概念,同时,又须要文科的抽象概念和计算方法,所以,其实人都有文理科思维,所以,其实人人都能够学习计算机,都能够学习编程,就看你怎么使用本人的能力和可能保持多久,这才是问题要害!留神:版权:本站应用「CC BY 4.0」创作共享协定,未经作者批准,请勿转载;若经批准转载,请在文章显著地位注明作者和出处。

February 24, 2022 · 1 min · jiezi

关于编程思想:编程是什么

引言咱们为什么提出“编程是什么”这个问题,是因为咱们想要解决编程的相干问题。如果问题都了解谬误,那就难以解决问题了。那为什么不是提出别的问题?因为对于要探讨的主题做好定义是十分重要的,就像咱们学习任何一门语言的时候,根本就是先学“我是谁”的句式。做好了定义,那就分明接下来要用什么办法去解决问题。如果我说“我是Java程序员”,那听者必定会在脑海里找寻“Java程序员”相干的话题或者问题。但如果我明明是Java程序员却说“我是律师”,那就是坑骗别人了。因而,“编程是什么”这个问题搞不清楚的话,就会被误导。 咱们都晓得“编程”指代的是编程式解决方案的整个设计和实现流动。但这定义的档次过于宏观了,就像我说“我是中国人”一样难以了解到更多的信息。上面咱们将参考Peter Naur在1985年写了《Programming as Theory Building》(编程就是实践构建)一文,来具体解说。此文被收录在他的著述集《Computing:A Human Activity》(Naur 1992)。 编程是什么,Peter Naur的答案如下:编程就是实践构建。这句话是从解释流动的档次来看的,换句话说,就是程序员们通过编程,对手头的问题造成或达到某种见解、实践。而不是把编程当作是程序或某种其余文字资料的生产过程。 首先,咱们来看“把编程当作是程序或某种其余文字资料的生产过程”的观点。试想下理论开发进去的程序(就是本人写进去的代码)和本人一开始构想的需要阐明、设计等文档外面的是否统一。是不是总是要通过屡次的测试和代码修复才可能达到统一?而且就算统一了,这样的统一是绝对的,编程总是要思考得更多,例如编程语言的技术限度、数据库技术的限度、硬件能力的限度等等事实的因素。非功能属性(即品质属性)的文档阐明的观点更是难以与编程的后果(运行程序)相符的。因而,“把编程当作是程序或某种其余文字资料的生产过程”的观点是有问题的。接下来咱们从程序修改角度来分析“把编程当作是程序或某种其余文字资料的生产过程”的观点存在的问题。 程序修改置信有不少人会纳闷:为什么是先从程序修改的角度?因为承受一直变动的外部环境所要求的程序修改是编程的基本局部。通过程序修改的角度来看“把编程当作是程序或某种其余文字资料的生产过程”的观点,也是一种逆推的办法,即如果编程是生产过程,那么生产过程的成绩(程序代码、文档)应该能反对程序修改。就像工厂车间能够依据生产线的须要更换机器或者人员一样。上面咱们援用一些实在的案例来看程序修改的问题。 案例1讲是A组人员开发了一个编译器能够运行在X机器上,B组人员在这里根底上进行扩大开发使其能够运行在Y机器上,即使A组人员给B组人员提供了残缺的程序文本、补充文档和A组的集体倡议,并且B组人员具备很高的主动性,但B组人员并没有对设计有着深刻的见解,因为A组人员发现B组所倡议的解决方案捣毁了编译器原有的弱小能力和简略性。又过了几年,在没有A组的领导下,另一些程序员接管了B组开发的编译器,后果它变得毫无效率了。因而,咱们能够得出这样的论断:程序代码和文档不足以作为某些最重要的设计思维的载体。 案例2讲是一个监督工业生产流动的大型实时零碎的装置和故障诊断。零碎的每次交付都须要去适配非凡环境的机器设备。存在全职的程序员A组始终从设计到交付都亲密地关注这个零碎,而且在诊断故障的时候,简直排他地依赖这个零碎上已有的常识和写好的正文代码,而不能设想有任何文档可能对他们有用。简略来说,就是利用已有常识进行排除法去诊断故障。而负责零碎的非凡装置的程序员B组,只管他们收到来自程序员A组的文档和对于应用的齐全指南,但他们依然常常遇到一些问题,须要向程序员A组征询。起因在于程序员B组对已有文档的不充沛了解,但程序员A组却能够轻易地解决这些问题。 因而,论断就是:至多对于某种大型的程序来说,继续的适应、批改和纠正其中的谬误,在实质上依赖于某种常识,而只有那组严密地继续地放弃与这一零碎分割的程序员才领有这一常识。 那咱们先大胆地推论:至多从程序修改的角度来看,编程必须包含构建程序员的常识,并把它作为根底的局部。 程序员的常识是实践如果咱们认同“程必须包含构建程序员的常识,并把它作为根底的局部”,那么咱们应该把程序员的常识作为一个实践来正确地对待。 咱们先来看:程序员的常识是什么?置信通过下面对于程序修改的探讨,咱们应该分明程序修改其实就是一种实际。既然程序修改依赖于程序员的常识,那这种常识就是实际进去的常识,也就是晓得了“怎么做”。那实践是什么?实践的根本释义就是:由实际概括进去的科学知识的有零碎的论断。显然,程序员的常识是实践。当然,这里对于实践的解释相当浮浅,只是为了能阐明问题,更深刻的对于实践的概念的了解能够参考《心的概念》(Ryle 1949)一书。 依照Ryle对实践开发出的概念,简略来说,就是:如果一个人领有这个意义上的实践,那么他就不仅晓得如何做某些事件,而且还能通过对所关怀的流动进行解释、论证、答复询问,来反对理论在做的这些事件。 由程序员构建的实践表白那既然分明“程序员的常识是实践”,那这样的实践是否能够依照肯定的规定表达出来以便于大家了解?这里先给出的答案是:在原则上不能。因为实践依赖于抓住真实世界中的情景和事件之间的某种类似性,这种依赖关系导致了某些领有实践的人可能把握某些常识,但在原则上他们却不能用规定的词汇把这些常识表达出来”。实际上,类似性难以使用准则的词汇表达出来,就像咱们难以说出香味的类似性一样。如果咱们说香味像桃子,那就不是一个准则的词汇,除非咱们建设了一套用水果来形容香味的准则,就像冷热的掂量应用温度计一样。 因而,从“编程是实践构建”的观点来看,“由程序员所构建的实践”具备超过其余产品(比方程序代码、用户文档、还有规格阐明等额定的文档)的位置。程序员的常识(即由程序员所构建的实践)至多在三个根本畛域中超过了文档所给出的常识: 程序员能解释:解决方案与它可能帮忙解决的事实中的事件的关系。即对于程序代码的每一部分和它整体构造上的每个特色,它匹配了事实中的哪个方面或流动。反过来,对于事实中的任何一个方面或流动,它们是怎么映射到程序代码中的。程序员能解释:程序代码的每一部分的成因。最终根据肯定都是程序员的间接的、直觉的常识或预计。因为依据手头相干的状况进行准则和规定的抉择,必须依赖于程序员的间接常识。程序员能解释:如何更好地批改程序。因为程序员领有实践,让其能够感知到新需要或Bug修复与编程实践经验的相似性,从而能够更好地批改程序。程序修改的问题及老本提出“编程是实践构建”的观点有一个重要的理由,那就是:想要建设一个对于编程的见解,而这个见解反对对程序修改进行正当的了解。 软件总会被批改,并冀望以尽可能低成本来批改程序。如果把编程视作代码生产,则主导的老本是代码操作的老本。但在“编程是实践构建”的观点中,这种论据齐全是谬误的。如果假如程序的灵活性十分高,即程序可能解决某种类型的外部环境变动,以至于基本不必任何批改,这样的话,老本就是最低的。但事实是内置在程序中的灵活性不能解决“让程序适应与事实中变动的环境”的须要。 在“编程是实践构建”的观点中,程序修改并不是实践的批改。一个领有实践的人必须曾经筹备好了去响应各种可能产生程序修改的问题和要求。因为领有实践的程序员通过洞察新需要与程序曾经满足了的那些需要之间的类似性,可能更好地批改程序。 如果没有正确地体会程序所依赖的实践,程序代码就会消退成了程序员所做的批改的后果。因为只有从“编程是实践构建”的角度,能力真正了解诸如简略和构造良好之类的代码品质。 程序修改的生命周期咱们再从程序修改的生命周期来看老本的问题。程序修改的生命周期有生存、死亡、复活,具体解释如下: 程序处于生存状态就是把握了它的实践的程序员团队保留了对于该程序的被动管制,特地是保留了对于所有批改的管制。程序处于死亡状态就是把握了它的实践的程序员团队遣散了。程序的复活就是新的程序员团队从新建设了这个程序的实践。这里程序的复活是产生老本的要点。但严格来说,不可能仅依据文档就能使程序复活(也就是从新建设程序的实践),必须接管程序的新程序员团队有机会于那些已把握了这一实践的程序员们密切接触。因为只有这样能力相熟该程序在相干真实世界的更广阔的情景中的地位,能力理解“这个程序如何工作”和“如何在该程序的实践下解决那些非凡的程序反馈和程序修改”。因而,从“编程是实践构建”的观点来看:在程序复活之前,该当摈弃现有的程序代码,还该当给这个新组建的程序员团队一些机会去以全新的形式解决所给的问题。这样一个过程更可能产生一个可行的程序,而不是程序复活,而老本不会更高,反而可能会更低。即使是在用一个演进式的程序员团队来使这个程序始终放弃在生存状态的时候,也可能产生相似的问题,起因是:每个程序员的能力和背景教训不同,特地是当这个团队始终在不能防止人员流动的状况下运行的时候更是如此。 程序员的定位咱们从“编程是实践构建”的观点中能学到什么呢?集体认为最重要还是程序员本人的定位问题。编程流动的首要后果是“程序员们持有的实践”。而在真正的实质上,这一实践就是每个程序员精神财富的一部分,因而,咱们必须摈弃“程序员是程序生产流动中很容易替换的部件”的概念。相同,必须把程序员看作是这一流动的一个负责任开发者和管理者,而计算机只是这个流动的一部分。为了满足这种格局,作为企业雇主,必须给程序员一个业余的定位,因为编程依赖于程序员常识上的精通;作为程序员,必须以“进步对实践形成的理解能力”为主,以“精通概念、数据体现和数据处理等技能”为辅,来晋升本人的能力。 论断编程就是一种流动。从解释流动的档次来看,编程就是实践构建。这样一种观点引出了程序修改的生命周期的概念,探讨程序复活的不可取,倡议是从新构建新的实践。进一步推论是:必须把程序员定位为“有责任的永恒开发者和这一流动的管理者,计算机常识其中的一部分”,而程序员的自我的晋升重点在于实践构建的练习,次重点是精通概念、数据体现和数据处理等技能的学习。

January 27, 2022 · 1 min · jiezi

关于编程思想:面向过程与面向对象

面向过程了解一种以过程为核心的编程思维。剖析出解决问题所须要的步骤,而后用函数把这些步骤一步一步实现,应用的时候一个一个顺次调用就能够了。 特点自顶向下的编程 「函数是一等公民」,如下图所示 长处性能比面向对象高。 毛病没有面向对象易保护、易复用、易扩大。 利用场景性能要求较高且规模较小的问题。 面向对象了解一种以对象为根底的编程思维。把形成问题的事物分解成各个对象,建设对象的目标不是为了实现一个步骤,而是为了形容每个事物在整个解决问题的步骤中的行为和状态的变动。 特点高度实物抽象化 「对象是一等公民」。 长处易保护、易复用、易扩大,因为面向对象有封装、继承、多态性的个性,能够设计出低耦合的零碎,使零碎更加灵便、更加易于保护。 毛病性能比面向过程低(面向更高的逻辑形象层,使得面向对象在实现的时候,不得不做出性能下面的就义,计算工夫和空间存储大小的都开销很大)。 利用场景规模较大且对维护性、复用性及扩展性的要求较高。 三大特色封装将形象失去的数据和行为(或性能)相结合,造成一个“黑盒”,仅对外裸露一些可调用的公共办法。 继承继承机制容许创立分等级档次的类。继承就是子类继承父类的特色和行为,使得子类对象(实例)具备父类的实例域和办法,或子类从父类继承办法,使得子类具备父类雷同的行为。 多态同一个行为具备多个不同表现形式或状态的能力。一个类实例(对象)的雷同办法在不同情景有不同表现形式。多态机制使具备不同内部结构的对象能够共享雷同的内部接口。 五大准则繁多职责准则(SRP)一个类应该有且只有一个去扭转它的理由,这意味着一个类应该只有一项工作。 凋谢关闭准则(OCP)对象或实体应该对扩大凋谢,对批改关闭。 里氏替换准则(LSP)在对象x为类型T时f(x)成立,那么当S是T的子类时,对象y为类型S时f(y)也应成立(即对父类的调用同样实用于子类)。 依赖倒置准则(DIP)高层次的模块不应该依赖于低层次的模块,他们都应该依赖于形象(即具体实现应该依赖于形象,而不是形象依赖于实现)。 接口隔离准则(ISP)不应强制客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不应用的办法,应用多个专门的接口比应用单个接口要好的多。 异同点1.两者都是软件开发思维,先有面向过程,后有面向对象;2.封装性:面向过程是封装的是性能,而面向对象封装的是数据和性能,并且面向对象具备继承性和多态性;3.编程特点:面向过程是自顶向下的编程 「函数是一等公民」、面向对象是高度实物抽象化 「对象是一等公民」。 举个栗子造汽车汽车的组成有发动机、轮胎等诸多配件组成,造汽车的步骤假如个别是先造发动机、再造轮胎、再造车架等配件、最初实现汽车的组装。 面向过程将造汽车步骤别离封装成函数实现(如造发动机makeEngine()、造轮胎makeTyre()、组装汽车assembleVehicle()等),而后依照步骤顺次调用实现造车(makeEngine()->makeTyre()->assembleVehicle())。 面向对象剖析造汽车这个问题中波及的事物(汽车、发动机、轮胎等),别离为其封装构建一个类,如汽车Vehicle(属性:色彩、发动机、轮胎等,办法:启动、刹车等)、发动机Engine(属性:色彩、材质、分量等,办法:运行、进行等)、轮胎Tyre(属性:色彩、材质、分量等,办法:滚动、进行等)。造车的话就是先创立一个汽车对象,而后创立并赋值汽车组成部分(属性)须要的对象,一个汽车就造好了。 五子棋五子棋的根本游戏规则大家都理解,就不过多的赘述了。 面向过程首先剖析问题的步骤:1.开始游戏 2.白子先走 3.绘制画面 4.判断输赢 5.轮到黑子 6.绘制画面 7.判断输赢 8.返回步骤2 9.输入最初后果。把下面剖析进去的每个步骤用不同的办法来实现。 面向对象剖析五子棋这个问题中波及的事物(玩家、棋子、棋盘、规定),别离为其封装构建一个类,1.五子棋分为黑白单方且这两方的行为截然不同 2.棋盘零碎,负责绘制画面 3.规定零碎,负责断定诸如犯规、输赢等。第一类对象(玩家对象)负责接管用户输出,并告知第二类对象(棋盘对象)棋子布局的变动,棋盘对象接管到了棋子的变动就要负责在屏幕下面显示出这种变动,同时利用第三类对象(规定零碎)来对棋局进行断定。 Q&A面向过程和面向对象的区别是什么?问题剖析:该问题次要波及到面向过程和面向对象这两种编程思维的根本定义和异同点。 问题答复:面向过程是一种以过程为核心的编程思维,将问题分解成一个一个的步骤,而后用函数去封装各个步骤的性能,再依照顺序调用函数就行了。面向对象是一种以对象为核心的编程思维,将问题波及到的事物分解成各个对象,建设对象的目标不是为了形容某一个步骤,而是为了形容每个事物在解决问题的步骤中波及的行为和状态的变动。面向过程是一种自上而下的编程,函数是一等公民;但面向对象是一种高度抽象化的编程,对象是一等公民(特点)。面向过程封装的是性能,而面向对象封装的是数据和性能并且面向对象还具备继承性和多态性(封装性)。尽管面向过程和面向对象都是编程思维,然而先有面向过程才有面向对象,其实面向对象的行为封装的执行办法外部其实也是针对这个具体行为或步骤的面向过程实现。 用面向对象的思维谈一谈面试的“过程”?问题剖析:该问题次要想考查的是候选人对面向过程和面向对象这两种编程思维的了解并且是否灵活运用在实际场景,而不仅仅是背诵概念(这种面试题十分好,不仅能看到候选人对相干实践概念的了解还能考查其对相干概念的了解水平以及实际中对相干概念的利用状况如何)。首先必定是先要晓得面试的步骤(个别状况-电面)=>1.面试官打电话给候选人预约面试工夫2.在面试工夫面试官和候选人都已在线,开始面试3.候选人做一下集体的自我介绍4.面试官开始发问5.候选人依据面试官的问题作答6.返回第四部,循环执行直到面试官不再发问7.面试官会在正式的面试发问环节完结后提出候选人是否有什么问题的问题8.候选人发问9.面试官作答10.面试完结。 问题答复:用面向对象的思维剖析这个“面试过程问题”,波及的事物有面试官,候选人,通信工具(电话),面试官属性有名字、身份标识(面试官)、候选人(在此种场景下是一个面试官面试一个候选人),行为有应用通信工具、提出问题、解答问题、完结面试;候选人属性有名字、身份标识(候选人),行为有接听通信工具、提出问题、解答问题。整个过程就十分清晰了,面试官应用指定的通信工具分割候选人,候选人应答实现一对一连贯建设,面试官对候选人发问,候选人对面试官的指定问题作答,候选人对面试官发问,面试官对候选人的指定问题作答。面试官和候选人都没有新的发问后,面试官完结面试。 参考《Java核心技术·卷 I(原书第10版)》面向对象与面向过程的实质的区别深刻了解面向过程和面向对象编程思维

November 14, 2021 · 1 min · jiezi

缓存击穿-雪崩

缓存击穿:缓存中不存在key,大量访问穿透到DB造成系统崩溃     DB中不存在key,可能为恶意攻击         设置key = null 或 "",且较短的过期时间         设置bitmap,使用布隆算法过滤一定不存在的key     DB中存在key         高频访问的热点数据设置永不过期,可通过定时脚本更新         大量请求访问同一个key,通过锁控制只有一个请求读取DB,其他请求等待或直接返回 缓存雪崩:缓存中大量key同时过期,DB压力暴增     设置随机过期时间,确保key不会同一时间过期

June 28, 2020 · 1 min · jiezi

幂等

幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变 幂等的实现方案系统状态机控制幂等(旧状态无法提交) 页面通过 loading、跳页 控制无法重复点击 后台token校验 token由前端生成,存redis,校验token存在即为处理过或处理中的请求 setnx token校验,token由服务端生成,先申请,存redis,使用时删除成功为有效请求 del 参数校验 入参存redis,重点参数一致认定为相同请求(例如个人三要素验证) 在一定时间范围内,参数一致认定为想通请求(例如创建订单) DBselect & delete 天然幂等 insert 先查询是否存在再插入 使用联合唯一索引 使用中间去重表 update 业务场景并发低,set a = 123 认定为幂等 业务场景并发低,使用version来控制幂等 业务场景高并发 对于自减 where count - 1 > 0 对于自增 通过后端代码去重 或拆表使 count = select count(0) from table_b 由高并发引发的联想通过MQ进行削峰,引发重复消费问题 通过db或redis,存消息id解决重复消费问题 通过消息参数做业务去重(后台 参数校验)

June 28, 2020 · 1 min · jiezi

CAP

数据一致性强一致性在任意时刻,所有节点中的数据是一样的。同一时间点,节点A中获取到key的值与在节点B中获取到key的值应该都是一样的。 弱一致性:与强一致性需要同时保持所有节点的数据是一样的不同,弱一致性允许同一时间点在节点A与节点B上面读取的数据不一致,如多级缓存。 最终一致性是弱一致性的一种特例,保证用户最终能够读取到某操作对系统特定数据的更新。但是随着时间的迁移,不同节点上的同一份数据总是在向趋于一致的方向变化。简单的理解为在一段时间后,节点间的数据会最终达到一致状态。 CAP 在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition Tolerance(分区容错性),三者不可兼得。 一致性(C)在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本) 可用性(A)在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性) 分区容错性(P)以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。 Partition tolerance 分区容错 大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。 上图中,G1 和 G2 是两台跨区的服务器。G1 向 G2 发送一条消息,G2 可能无法收到。系统设计的时候,必须考虑到这种情况。 一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。 Consistency 一致性 意思是,写操作之后的读操作,必须返回该值。举例来说,某条记录是 v0,用户向 G1 发起一个写操作,将其改为v1。 接下来,用户的读操作就会得到 v1。这就叫一致性。 问题是,用户有可能向 G2 发起读操作,由于 G2 的值没有发生变化,因此返回的是 v0。G1 和 G2 读操作的结果不一致,这就不满足一致性了。 为了让 G2 也能变为 v1,就要在 G1 写操作的时候,让 G1 向 G2 发送一条消息,要求 G2 也改成 v1。 ...

June 24, 2020 · 1 min · jiezi

如何开始编码

首发于我的博客网站(prajna.top) 欢迎大家前去交流,有pdf版本。 本文主要是从应用的角度出发,分别阐述操作系统接口,计算机语言,文件系统等背后的一些知识,规范,原理,设计思想,应用法门,让初学者对编码有一个整体的,全局的认识,有一个物理的视角,找到自己的起点。 前言写这篇文章主要是基于自己大学的经历,当时抱着一腔热血去学计算机编程,可是当把c/c++语言,数据结构,操作系统,计算机组成原理等课程都学完后,却发现自己似乎什么也不会,只会printf打印一些字符串。那段时间真的好苦恼,特别想做软件,却不知从何开始,也不知道该如何去使力,蹉跎了好久,浪费了大量的时间。 造成这种现象的主要原因,一是自己缺少那种天赋,二是教学过于侧重基础和理论,每门课程只涉及到一个局部,没有一门课程把这些串起来。我不了解语言的基础库,除了printf后,其它API都不会用;也不了解具体的操作系统平台的API;虽然学了TCP/IP,socket的具体使用却又不清楚; 至于像fat,ext等磁盘文件系统的格式,那就更遥远了;我甚至还不清楚计算机语言和编译工具的关系;更要命的是,我还不知道自己不知道这些。说白了就是理论同应用脱节,虽然大学也安排了课程设计,实验和实习,但都只是走了过场,也没有人来指点一下,该看些什么书籍。大学读完,就知道拖拉几个控件做一个窗口,连接一下数据库。 windows + VC屏蔽了太多的技术细节,可惜大学期间接触的偏偏就是它,对用户这个是好事越傻瓜越好,可是对计算机的学生就要了命了。自从我转投到linux开源世界后,终于才发现了什么是自由,什么是编程。当阅读linux源码碰到不理解的地方,可以直接修改源码加上打印,分析kernel流程。对比着minix的源码来学习操作系统结构原理,那些概念就变成实实在在的数据结构和算法,动手写一个minix的驱动,微内核和宏内核区别就一目了然了。强烈建议,想学习编程的同学都去拥抱开源世界,然后,再回到自己感兴趣的或工作相关的领域。 计算机语言说白了就是工具,关键还是你要做什么,这样就涉及到了应用,以及专业背景知识。如想做驱动编程,离不开对操作系统驱动架构的了解;想做一个磁盘分区合并,那需要了解文件系统的格式;做个播放器吧,那对视频文件格式,编码格式,编解码API的了解必不可少。 随着你对软件系统了解的深入,会发现其实一切都是协议。 http 是一套web 通讯的协议;计算机语言是开发工具提供的协议; 操作系统是内核空间与应用空间的协议..., 这些协议被各种规范约束--并形成了各种技术。 所以,每种技术的背后都一套协议,规则和思想。了解这些才能算真正了解了相关技术。 在这篇文章里面,我以GNU/Linux作为平台,从应用的角度出发来把相关的课程来串一串提供一个“物理视图”,让初学者有个全局的认识,能够有一个方向和切入角度,至少知道该找些什么资料来看。 操作系统接口它是内核对应用空间提供的一套协议,主要包括: ABI可执行文件的结构。系统调用(system call)。sysfs 文件系统接口(linux kernel)。ELF是编译, 链接生成的,执行的时候,由ld 解析,加载在到内存,最后控制权交给程序入口代码,程序开始执行。因此,它提供了2类视图:链接视图和执行视图。 从链接视图上看,ELF由众多的 Section组成,编译器先把源码编译成.o文件,主要是提取函数,全局变量等生成符号表,把它们填充到相应的 Section里面去。 在这个阶段,所有的符号都是没法定位到地址的。 Link的时候,对.o文件进行合并,对各个文件内的符号进行重定位,安排它们的地址,如下图所示, link完成后,g_u8 和 g_flag2都有地址了。 对于动态链接的函数,在link阶段没法安排地址,需要放到 dynsym Section里面去,在 ld的时候,来进行定位 -- 这就是所谓的 "函数重定位"。linux系统提供了可执行程序readelf来解析 ELF文件格式,我们可以使用它来了解一下ELF文件的一些通用的Section。 bss 是没有指定初始化值的数据, 有些编译器会默认全部初始化为0。data 全局变量初始化。text 代码。init 程序初始化运行的代码。fini 程序结束运行的代码。symtab 符号表, 它是源码编译生成的产物,可以为代码运行,调试提供信息。 属于辅助性质,不参与 load 和运行, 可以用 strip来删除掉。'offset Align' 是各个Section在ELF文件内的偏移地址,我们以二进制的方式打开ELF文件,根据偏移地址,就可以查看相应Section的二进制内容。 从下图中可以看到 .interp的内容是 "/lib64/ld-linux-x86-64.so.2", 上面这些就是编译,链接生成ELF文件的过程:编译器以源文件作为输入,先提取各个文件的全局变量和函数,生成符号表,再把它们链接到一起,链接的时候对各个符号进行定位,分配地址。对于动态链接库的函数,则推迟到'加载'程序到内存的时候进行定位。编译链接后,代码和数据分散到了相应的section里面,程序加载的时候,需要把Section 合并成Secgment,然后,以Secgement为单位加载到内存页面里面去,我们来看一下Segment的结构。 ELF有9种Segment,其中比较核心的是 -- ...

November 5, 2019 · 2 min · jiezi

软件开发什么是过度设计

软件设计(架构)往往在项目开发中起到非常关键性的作用,至少它是能够工作。良好的软件设计包含了:灵活性、可伸缩性、可行性、可复用性、安全性,通过该一系列的定义,使我们影响到了软件功能的设计和特征。 (一)、什么是过度设计过度设计一词在英文中称为"over design",over意思是太多,design意思是设计、构思,通过教科书上面的解释,意味着你设计的或构思的太多了,即为过度设计。 什么是过度设计?设计出来的系统比恰到好处要复杂或臃肿的多,过度的封装、继承、接口或是大量的无用配置方法,其实就是用户需要一把杀鸡的刀,而你却设计出了杀牛刀或是电锯。 过度设计通常来自于开发者将问题过于复杂化或是前瞻性欠缺。 在我们日常所犯的错误中,大部分是来自于前者,至于后者的欠缺,需要一定的项目经验和洞察力来支撑,能够合理的预判和考虑需求会哪个方向发展。 在前者,问题复杂化会引入大量额外的代价,如成本上升,系统缺陷增大、提升维护成本、降低系统性能。而高性能和可维护性都是系统的隐性需求,如果这些也没实现好,那就可能属于设计错误。 但是从客观角度来看,能够进行过度设计的,多半设计能力高于设计不足的,过度的设计改回来的成本也比设计不足的改过去的成本低的多,在此需要更多地去权衡"利与弊"。 (二)、过度设计案例以系统充值为例,最初你设计的系统只需要一个支付宝功能,你的数据库设计如下: id primary key int //主键user_id int//充值用户status int //-1充值失败,0充值中,1充值成功order_no string //第三方支付系统订单号amount decimal //金额但是没过多久,你的系统需要接入另一个支付系统-微信,需要区分用户是通过微信还是支付宝充值的,于是你的数据库设计便成了以下模样 id primary key int //主键user_id int//充值用户status int //-1充值失败,0充值中,1充值成功order_no string //第三方支付系统订单号platform string //第三方交易平台amount decimal //金额但是你想了下,感觉可能以后需要接入银联支付,需要记录是哪张银行卡支付的,然后你又想了下,既然已经接入了银联支付,那顺便就再完善以下,支持国际支付,如美元充值,港币充值,然后需要记录上当地充值的汇率及当地支付时间,最终你花了一天的时间,将数据库改成了这样 id primary key int //主键user_id int//充值用户status int //-1充值失败,0充值中,1充值成功order_no string //第三方支付系统订单号platform string //第三方交易平台amount decimal //金额currency string //货币:CNY USD HKD bank_id int //银行卡IDrate decimal // 充值汇率local_pay_time //当地支付时间完成基本设计后,你花了4周的时间完成编码和测试,最终交付了上去,然而系统的初衷只是需要简单区分的是微信充值或支付宝充值,而因过度设计带来的额外成本和缺陷是非常巨大的,为此我们需要尽力让自己做的恰到好处并且避免过度设计。 (三、)如何避免过度设计?避免过度设计的最佳方法就是“不要设计的太远”,未了解实际未来,就做出了各种预设和判断,为系统增加了额外的负担。 正如scrum(敏捷开发)所倡导的Evolutionary Design(演进式设计),将每一次的重构和迭代都映射和更新到最新的设计中来,从而最大限度的满足系统的功能性需求和非功能性需求。 当你手里握着一把锤子时,不要把所有看到的,都当成钉子。

September 8, 2019 · 1 min · jiezi

程序员的修炼我们为什么会编写BUG

在最近的一周,我维护的业务系统出现了很多坏毛病,一周七天crash掉了4次,每次都需要都是因为一点很小的问题,触发了蝴蝶效应,导致整个系统全盘崩溃,于是产生除了叙述本篇的想法,当然这并不是为了掩盖我在Coding上的一些细节处理和职责疏忽,只是为了从根本的细节上去分析这些问题。 (一、)为什么会产生BUG首先我们需要尝试理解一下什么Bug? 关于bug的解释 bug 是指任何计算机程序或硬件系统中的错误,故障或缺陷。错误会产生意外结果或导致系统意外运行简单来说:bug就是程序出了问题,产生了意外的结果,没有按照预期的结果去运行。 产生Bug的原因有很多种: 开发者水平太低不同的编译及运行环境与需求方沟通不到位马虎大意、考虑不周放飞自我,Coding全靠自嗨选择了错误的或者运行不稳定的第三方库以上原因总结,主观和客观因素都会影响到Bug的产生,正如误差不可避免一般,我们应该对自己写出的代码进行测试、分析、"沟通". (二、)如何尽量避免Bug鉴于以上bug产出的原因,我们可以通过这些一些对策来避免Bug的产生,下面是一些常见原因分析和处理对策。 1.开发者水平太低 在进行系统的构建中,部分开发者可能通常因为开发经验过少,或者语言不熟悉,会编写错误的代码,然后未经过代码测试和审计,便进行提交和上线操作,导致了异常的引发 解决方案: 如果是语法错误,可通过一些ide的代码检测器,或者语法检查来检测代码可否正常运行.如果是PHP等弱类型语言,可使用静态代码扫描工具来发现程序中明显的语法错误.编写足够的测试用例,覆盖整个模块的语句请求你的伙伴进行CodeReview(代码审计),来改善代码的质量和发现代码中的缺陷2.不同的编译及运行环境 因为业务的拓展和服务支持,需要部署多个不同的运行环境中,如:转账系统,你在测试环境中转账了1000元给用户小明,小明却在生产环境中收到了这1000元,并成功进行提现,往往因为没有环境判断,导致了失误的操作! 解决方案: 1.在代码中多进行注释说明,标明哪些函数会在其他环境中操作和运行 2.加强环境逻辑判断 以下是我在使用的一些标注和说明,其他开发者或我本人再次阅览该代码时,就会得到一个清晰的运行结果. /** * 执行该函数时,会根据env环境进行处理,详细如下 * prod(生产环境):会启动队列对视频进行转码、截图、写入到生产数据库中操作. * staging(预演环境):不会启动队列,但会写入staging数据库中 * test(测试环境):会启动队列对视频进行转码、截图、写入到测试数据库中操作. */$video = $this->uploadVideo($file);$queue = $this->videoQueue($video);3.与需求方沟通不到位 这是经常程序员与产品对撕的一个很重要原因,TA想要A,而你却做出了B,于是你们产生了很大的争论 解决方案: 多进行沟通,需求进行反复确认,不要上手就进行编码,先进行分析。通过PM系统,留存需求规划与变更记录,以便每一次业务更改,都得能与系统中的问题对上号.4.马虎大意、考虑不周 编码时以为问题很小,修改代码,不走调试与测试流程,直接上线. 解决方案: 不要盲目过于自信,相信自己的主观判断,一定走测试流程,确保改动无误!(这是我之前经常犯的错,然后系统出了问题,我的fix commit从1变成了N....)CodeReview(代码审计),这是一个最好的办法,当然需要耗费不少的人力,但是能最大的去降低缺陷和错误.5.放飞自我,Coding全靠自嗨 解决方案:无 这类朋友不适合做开发者,适合去做创造者!6.选择了错误的或者运行不稳定的第三方库 有时候为了省略接入时间,往往会忽略掉一些大型库,因为业务的支持只用到了一小部分,所以我们有时候会去选择一些mini库,最后由于不稳定或方案不成熟,出现错误的运行结果 解决方案: 如果业务级别比较高的话,不建议采用冷门或者无人问津的mini库使用,因为出现问题的损失会更大.进行反复测试,开发人员对核心代码进行阅览,确保正常无误.自我组织编写或实现,但是学习和开发成本比较高,小型规模不建议采取.(三、)多与代码进行"沟通"“橡皮鸭调试法”是我在阅读《编写可读代码》一书中看到的一个技巧,我在一个人开发的时候会使用这个技巧,我认为是一个不错的选择. (四、)总结我们为什么会编写BUG,如果没有BUG?开发和测试不就失业了吗?当然这只是一句玩笑话。在此引用知乎上一句很有意思的话. 编码也亦如此,因为很多主观和客观的因素,导致程序执行了错误的逻辑,产生了不如预期的结果,作为一个合格的开发人员,我们应该尽力确保程序稳妥运行,减少失误和异常。 正如CZG提到的"你写的每一行代码,都是你的名片",我们每个人都义务去维护好这张名片,让其他人对这张名片充满敬畏之心,而不是"what shit code",诸君共勉!

August 19, 2019 · 1 min · jiezi

如果在我学编程时有人告诉我这些该多好【译】

趁着元旦休假+春节,尝试把2018年期间让我受益的一些文章、问答,翻译一下。欢迎指正、讨论,希望对你也有所帮助。原文链接:Things I Wish Someone Had Told Me When I Was Learning How to Code说明:这一篇最没有干货学习编程前,思考下你想要用代码编写什么要知道,如何编写代码主要是关于如何构建事物,了解你的最终目标会让学习路径更将清晰。如果你的目标只是“学习编程”,而不清楚最终程序的种类,以及它们如何让生活更美好,那你可能会觉得过程充满沮丧。我有点惭愧地承认,我学习计算机科学的动机之一是想证明自己很聪明,希望能够获得聪明人才能从事的工作。我也喜欢数学和理论(这本书曾在我敏感的年龄引起了我的注意),编程与之不谋而合。不过,在我找到将技术与我真正喜爱的事物(比如音乐和文学)联系起来的方法之前,这种状态维持不了多久。那么,你学习写代码是为了编写什么?网站?游戏? iPhone应用程序?成立一个创业公司一夜暴富?交互很牛的软件?还是希望能够给领导留下深刻印象,亦或用脚本跑一些繁琐的任务,这样你就能忙里偷闲看水獭图片了(译者注:不是很懂这个趣味)?也许你只是希望更有竞争力,在简历中能写上会编程,或者学习编程只是你的一项自我教育的计划。所有这些都是值得的目标,不过你要找到属于自己的那一个,研究明白。编程并不神秘其实编程跟其他技能没啥区别。就像学英语,先学词汇和语法。也有点像数学,特定函数解决特定问题。还有点像各种工艺和制作技术,随着技艺的发展,针对不同场景和任务开发出不同的技术、工具,也总结出不同情况下的最佳实践,随你选用。这家伙(非常聪明,我很喜欢看他的文章)觉得,在编程领域,真正的程序员和其他人之间有一条明显的边界线,边界线区分出了两拨人的编程智慧。这条线就包括指针和递归。我在学校学到的指针和递归知识,当我学会它们的时候,仿佛颅内高潮了——正是这种智力上的愉悦感,让我想要深入学习计算机科学。但是,在课堂之外,需要学习新概念才能完成项目的机会越来越少,帮人越来越多地完成有意思的项目,可后来几乎都学不到新的概念了。没有必要怀疑自己是否足够聪明。当然,项目越复杂,需要的水平也越高,但这些在其他领域也一样。除非写代码成为你的本职工作,否则你也不必把递归理解的很透彻才能完成项目开发。第一次运行不起来很正常第二次、第三次可能还是跑不起来第一次学习编程,你很可能遇到这样的情况:认为自己已经正确地写好了配置,也检查再三,但是代码仍然运行错误。你也不知道从哪入手修复,错误信息(能有错误信息就算幸运了)也显示“去你的吧”。很可能在你就在这放弃了,因为你认为自己不是这块料。我深有同感,在我第一次编译运行C++代码的时候,我得到的错误信息是“segmentation fault”。其实这种经历对所有水平的程序员都很常见,甚至跟你的智力水平、编码经验都没啥关系。作为初学者或者老手都会遇到这种情况。主要的区别是,你要如何应对这种情况。我发现,编程新手和有经验的程序员的最大区别是信念:相信程序出错时出于逻辑错误,是有迹可循的;相信bug可以被解决,相信总会有途径解决眼下的问题。相信让代码正常运行的方法虽然暂时没找到,但耐心钻研,肯定能让程序跑起来。总有人会说你这样做不对花括号另起一行,花括号不要另起一行。用tab缩进,不要用tab缩进。你应该使用存储过程,不过实际上存储过程没啥用。你应该写注释,好代码不需要注释。这些互斥的建议会一直伴随着你。问题总是存在多解的,没有唯一正确的方法。很多程序员喜欢推广他们的个人偏好,但是选择并不唯一。大家一直告诉我我是错的,而我一直试图弄清楚错在哪,这让我早期职业生涯感到压力的一个方面。团队协作过程更是免不了有人反对你的编码方式。有时候反对你的人是对的,你要深入了解,看看为什么对,为什么错。有的时候呢,你才是对的,反对意见只是想挑起一个毫无意义的争论,那就直接忘掉它,按着编码规范该怎么写怎么写。也总有人会说你不是真正的程序员HTML不是真正的代码。如果你不用vi,那算啥写代码。真正的程序员都会写C,没人做Windows开发。有些东西你永远不会,你不应该干这个。你不是真正的程序员。编码这件事对不同人而言意味着不同的东西,相比过去,现在也发生了很多变化。而且很有意思的一点是,越来越成熟的工具、框架和开源代码,让新手和老手都更容易开发项目。使用这些工具和框架会被认为不是“真正的程序员”。这背后的阴谋是因为如果大家都称自己是程序员,那程序员这个称号就变得没意义了。这种自设门槛的行为害处颇大。放手去使用简单的工具开发项目吧。基于Stencyl或者GameMaker开发游戏没啥不妥,第一次编程是写HTML和Excel宏也没问题的。坚持该坚持的事情并不会有坏处。随着你越写越顺手,自然会发现更好用的工具。而且,大多数时候很少有人看你的具体实现,也不知道你用了哪些组件和工具。别让极客头衔阻碍你再读一遍前一段内容吧。我曾经非常担心自己被认为不是”真正的极客“,尤其是在学校,会担心我的着装、我的立场或者自己的读物和选择的软件不够极客。行胜于言关于如何学习编程的文章很多。你可以通过书本、交互式学习或者调试其他人的代码来学习概念。而且,你也有很多编程语言来选择哪个开始学习。市面上有很多自学编程的课程或者论坛。这些学习过程之后最常见的抱怨就是你可以快速地学习完初学者的知识,然后学习曲线突然就变得陡峭了。之后想要取得进步,变得非常困难。你知道如何写输出文本的代码,但不知道怎么开始写一个真正有用的项目。也可能会觉得自己学得比较僵硬,没有真正领会精髓,责备学习材料不够好。当你进入这个阶段,市面上大多数的教程和资料都没用了,因为那些材料面对的受众是非常初级的初学者。你当前的困惑是”不知道自己还不知道什么“,不知道自己接下来改如何更进一步。我想说的是,无论你按照哪个教程学编程,你都会碰到学习曲线变陡这堵墙。唯一的解决办法就是坚持不懈。你要学习更多的知识,尝试更多新鲜事物,逐步解决如何构建有用的项目。如果你都知道为什么学习编程,那你一定会找到接下来进步的路。梅花香自苦寒来,宝剑锋从磨砺出。我之前强调的信念,在这个阶段就会派上用场。保持耐心,坚持不懈,最终会找到答案。

January 17, 2019 · 1 min · jiezi

学习编程并不是学习编程语言

作者:zooboole英文原文:《Learning programming is different from learning a programming language》我们都是程序员,也是学习者。令人惊讶的是,如此多的人以为自己在学习编程,却已经步入歧途。你可能正在学习编程语言,而不是编程本身大家都知道计算机科学不是研究计算机,它反倒是利用计算机研究自动解决问题的。问题解决是计算机科学,不是编程。这就许多计算机科学专业的学生似乎不理解他们为什么要学习算法或数学的原因。如果你以前上过计算机科学课,你就应该知道我在说什么。因为你会注意到编程与编程语言几乎没有关系。问问自己为什么伪代码在这些课程中如此常见。但是,大多数自以为是的程序员总是落入陷阱。在意识到进行编程时到底什么是应该要做的之前,我们学习了几十年的编程语言。我自己也是受害者。我花了十多年的时间一点一点地学习各种编程语言。我学的越多,就越难以简单的方式解决问题。我以为是没有找到合适的工具。但问题是,当我甚至还不知道这个工作要做什么时,就去寻找合适的工具,而忘记了找出真正的工作是该做什么。编程语言的奇怪之处在于它们总是在不断发展。编程语言几乎每天都在变化,跟进很难。而大多数优秀的程序只使用了编程语言的一小部分。首先,学习编程语言的问题就像在学习木工之前学习如何使用木工锯,锤子和各种切割机器。木工需要注意:想法,可行性分析,测量,测试,客户行为。资深木匠感兴趣的事物不止于锤子和钉子。在他对这项工作的研究中,还需要时间来检查钉子、着色剂、木材等的质量。学习编程和学习编程语言的区别是什么呢?编程是通过一次下达指令来设置一个系统自动运行。我们每天都这样做。我们教我们的孩子,命令我们的士兵,服务我们的客户。我们给予或收到指示,以自由/独立的方式生活。父母不需要跟随并指导你在生活中所做的每一个动作。他们可能已经在生活的许多方面为你编程了。大多数学校和教学网站都会教授编程语言的语法。他们可以添加一些设计模式(当你忽略究竟是什么设计时)、一些算术计算。教你如何声明变量以及如何使用它们;教你如何声明数据类型以及创建它们。这并不能教你推理。但后来,您将会遇见推理方法。使用那些方法来学习,会让你觉得是浪费生命或者花了很多时间来学习编程。我们用编程来解决问题,编程语言是帮助我们达到目的工具。它们就象工具箱,我们称之为框架,帮助你组织你的思维。如果你正在学习编程且仍然无法设计和编写真实应用程序,那么这就意味着你正在学习编程语言而不是编程。我们经常会遇到想知道如何创建程序的学习者。对于程序员来说,程序是一个问题求解。在使用任何编程语言之前,他通过关键分析解决了问题。当你解决任何问题时,你可以用任何编程语言来编码。我们来看看平方求解的案例。为了求解平方,我们将它与自己相乘。我们可以用各种语言实现它,例如:C语言function square(int * x) { return x * x;}PHP语言function square ($x){ return $x * $x;}Javascript语言function square(x){ return x * x}Scheme(a Lisp dialect)语言(define (square x) (* x x))您应该注意到实现中只有语法是不一样的,解决方案是一样的。这也是你几乎可以使用任何编程语言的主要原因之一,在这种语言中你可以更轻松地构建任何类型的软件。编程可以让你更容易理解一门语言通常,问题出在人类语言,它充满了局限和错误。人类语言不能用来指令机器,因为它们不理解。你学习编程时,是在学习一种新术语和工具,来帮助你以计算机或其他程序员可以理解和同意的方式编写逻辑。通常,你将从简单且类似人类语言的符号–伪代码开始。它是从人类语言到计算机编程语言的良好过渡工具。这通常是为了避免浪费时间在具体的编程语言上,这样你可以完全专注于推理。通过它,你将发现构成良好编程工具(语言)的核心部分。你知道了真正需要的是什么、掌握了编程语言的核心目标。在编程实践过程中,你会不知不觉地就学会了这门编程语言。相关文章如何学习一门计算机编程语言

November 20, 2018 · 1 min · jiezi