这是一篇我';我想写一段时间了。在这篇文章中,我试图在一个非常基本的层面上解释我是如何看待编程的。我希望给新的程序员一些背景知识和更大的背景知识。帮助给那些经常被呈现为一堆要记住的狗屎赋予形式和结构
我不';不要试图教授编程本身。我不';t涵盖编写编译和运行程序所需的所有细节。这方面有很多指南。我不';我不会讨论计算机科学,也不会进入类型理论,尽管这两个主题都可以作为理解编程的非常难以捉摸的基础。我说的是";基础知识";很多,但我的基础不是';这不是一套总的理论。只是一袋工具
我的目标是传授一些关于如何为碰巧像我这样思考的人思考编程的一般启发
我开始编程的时候只是胡闹。对现有的javascript推特机器人进行微小的修改,打造我自己的,带有";去他妈的,随便什么都行";一种态度。渐渐地,我的修改变得更加雄心勃勃,几个月后,我可以自己编写程序了。几个月后,我发现有人做自由职业者webdev,愿意让我做初级合伙人。从那以后,我在工作中学到了一些东西
我通常认为理论是有用的背景知识,但挑剔的学习是第一手经验的一个很差的替代品。我";用我的双手学习,";就是我';我总是这么说。它有它的优点和缺点
34岁;工作知识";有些事情对我来说通常是微不足道的。我可以在几周内快速奔跑,这可能需要其他人几个月的时间,或者几个月的时间,这可能需要他们几年的时间。我只是猛击这个问题,直到它(理想情况下,这个问题)破裂
另一方面,这意味着我通常会在狂躁的爆发中获得力量。一旦进步变得越来越大,耐心和勤奋比鲁莽和暴力带来的回报更高,我很容易感到厌倦。我反反复复地思考我是否能够也应该训练自己变得更有纪律,或者我是否只是一个天生的异花授粉者类型的人,应该接受它
不管怎样,如果你对我的学习方式很熟悉,那么我对这些东西的思考方式可能会很有用。如果你在正式的课堂环境中,或在指导下,或通过勤奋学习,或通过学习精心编写的理论基础,学习得更好,那么它';这篇文章可能提供的唯一价值就是偷窥者';让我们来看看野兽的生活方式
我';我给至少十几个人做了这些讲座的零碎部分,他们';I’我经常来找我,征求关于如何开始编程的建议。我简要介绍了命令式编程中使用的基本抽象。然后我在上面瞥了一眼,可以从他们身上构造出抽象,在下面瞥了一眼,他们自己抽象出了什么
"抽象";在数学中,这意味着使用一个更高层次的概念,它承载的假设更少,但涵盖的案例范围更广。为了通用性,复杂性被去除了"抽象";在编程中,指用简写或方便的方式记录系统的基本工作原理。增加了复杂性,所以你可以假装它不是';不在那里。无论你如何打扮,当你';当你使用电脑时,你总是在延加宝塔的某个地方
掌握基本原理,了解它们的原理';重新组合成抽象概念,你可以边走边挑选几乎任何东西。我';我得到了一份我没有得到的工作';我不懂语言';I’我在写,希望在头几天里用一分钱就能学会。它';一旦你不再看到";那么这东西做什么" 然后开始看";啊,这就是他们所说的"这不是一种普遍存在的软件技能,但也不是一种罕见的技能现有的软件数量巨大。数百个生态系统,数千种工具,数百万个图书馆。如果你试着把它们都当作单独的东西来学习,那是不可能的。但是他们';我们都是用相同的基本部件制造的
我记得当我第一次开始编程的时候,看着招聘启事,看到几十种语言、工具、首字母缩略词、行话在必要时喋喋不休。java c#php perl js ajax ruby go mysql postgres敏捷scrum aws azure。真让人望而生畏!我几乎不知道一件事,我要如何学习10或15,只是为了开始工作?
现在我看着这些清单翻白眼。我知道他们都是什么,我';我已经涉猎了很多领域,我可以很容易地将其中任何一个学习到专业水平。很多都是一般事物的特例,很多都是同一事物的不同词汇,很多都是胡说八道。但所有这些都可以一目了然,通过实践学习
对于一个经验丰富的程序员来说,学习一项新技术通常不会有什么好处;看起来不像是协同学习、挫败尝试、奋斗和胜利。它看起来像是在阅读一个三段式的摘要,基于vibes对其进行编码,检查文档中的语法,并抱怨设计它的人一定是多么愚蠢
这篇文章的标题是";我是如何看待编程的";不是";你应该如何思考编程"因此,我不会容忍任何批评。我';我非常相信学习方式,我认为人们应该自然而然地采用最舒适的方式。如果接下来的一切听起来都是假的,我们可能只是有不同的观点
任何对curlybrackets语言有普遍了解的人都应该能够合理地获取这些材料。我承认有时我可能行动太快,有时我可能会陷入细节。在使其承载之前,我尽最大努力明确介绍技术术语。但是教育学很难,除非你';我们不断与新学员合作,it';It’很难记住你';我已经习以为常了
如果你觉得这篇文章很有价值,但部分内容是无法访问的,那么当你获得更多经验时,也许值得重温。我';我试图弥合概念之间的鸿沟,而不是试图教授概念本身。形式,而不是内容
我';我已经编程六年了。在那之前,我以做煎饼为生。唐';I don’我不相信有人说你需要一个高级学位、专业培训,或者从小就接触代码。你可以通过聪明和固执来学习编程
第一组基础是分配、流控制和范围。除非另有说明,所有示例均为c。唐';如果你';我们习惯于python或js。c很容易写。它';It’只是很难写正确
作业在概念上非常简单。您正在某个命名的存储器中存储一个值:
这里,值2存储在一个整型变量中。您可以通过名称';我给了你的存储空间:
代数上令人困惑的第二条线不是一个方程式。a的值,然后是2,从其存储空间中加载,并添加到b的值中。总和存储回空间b中,取代原来的3
要记住的是a和b不是对象。它们是我们存放物品的盒子的名称
通常情况下,一个程序从上到下逐行执行每一条语句,然后继续执行下一条语句。它会一直这样做,直到它遇到一个命令它停止的语句,或者一种迫使它停止的错误。但是一个只能继续或停止的程序不能做很多真正有趣的事情
一旦你有了一种基于当前状态改变未来执行顺序的方法,程序就会变得真正有表现力。实现这一点的机制称为控制流构造,最常见的两种是分支和循环
检查条件,并根据结果执行两个不同块中的一个。块中的代码可以是任意复杂的。可以按顺序检查多个条件,或者使用故障排除规则检查更复杂的构造,但它们在计算上是等效的
块无条件执行一次,然后检查条件,并基于结果,块将再次运行,或者流将继续通过循环正常运行。根据设置的条件,循环可以任意执行多次,可能是无限期的。还有其他一些循环形式检查顶部的条件,或者缺少条件,并强制您在嵌套分支中显式中止,但它们在计算上是等效的
在c语言中,连贯的执行单元被分组在块中。区块是独立的,封闭在卷曲支架中,可以有自己的独立存储空间。当到达一个块的末尾时,程序流在它所包含的上下文中继续。通过这种方式,块彼此嵌套,最终全部嵌套在主函数中。一个块可以从其外部容器访问存储区域,但一旦执行移动到块外部,所有的块';s存储丢失,但对外部存储所做的更改保留
区块的影响范围,包括其拥有的存储和可访问的存储,称为其范围。一个块有一个名称,可以从其他地方调用并传递信息,称为函数
int times2(int n){int x=n*2;返回x;}对于(int i=0;i<;10;i++){int x=times2(i);printf(";%d\n";,x);}
这里,for循环生成的i的递增值作为参数传递给函数int times2(int)。x在函数的作用域内计算并返回给其调用者。然后它被分配到x outside,一个完全不同的存储位置,恰好共享一个名称。int times2(int)内部的存储现在不存在了
它';值得注意的是,其他语言,尤其是javascript,对作用域的概念与c有所不同。我想指出的是,与编程中的许多事情一样,作用域是一种约定,而不是事实。大多数规则没有';真的";平均值";任何深层次的东西。他们没有';我们不能超越实施它们的环境
类型理论家、计算机科学家和范畴理论家对这样的断言会有截然不同的看法。但这并不是';目前与我们无关。我们到处搞清楚
学习规则,如何在他们的范围内比赛,以及何时打破规则。这是一种无意义但内在一致的方法。如果你沉迷于一切";真正的意思是";你会迷路的。除了编程语言,这对于导航许多由人类设计的系统非常有用
我喜欢把基本的编程概念想象成类似于武术武士。我们这些小时候尝试过空手道的人可能记得,从第一天开始,他们就期待着做翻转和飞踢。相反,我们被告知要一次又一次地进行同样的打击,没有任何变化。这就是kata:最基本的技术构件
我认为,kata是你真正擅长编程的秘诀。学习巴洛克风格的图书馆、庞大的插件生态系统、高耸的框架,到一个非常深的层次,是领域专家的职责。我尊重他们在';我们很擅长,但这不是我的工作方式
在我看来,要做到真正的好,你不';我不需要研究任何特别的大事。你需要反复练习和理解小事情,直到它们';你是第二天性。接口上的100毫秒延迟时间是响应瞬间的极限。你希望能够快速掌握基本知识。足够快以至于你不';I don’我根本不必去想它们
李小龙有一句精彩的话,我喜欢在适当的时候引用:
在我学会这门艺术之前,一拳只是一拳,一踢,只是一踢。在我学会这门艺术之后,一拳不再是一拳,一踢,不再是一踢。现在我了解了这门艺术,一拳就是一拳,一脚就是一脚。
在你了解一个领域之前,你就陷入了相同的困境。一切都是不透明的,没有区别的。随着学习的深入,您开始区分元素,将它们分组到类中,构建分类法。当你真正地、深刻地理解某件事时,很难坚持这样严格的区分,但更容易感受到。你可以讨论和构建模型,但你认为这些模型是偶然的,不充分的
在分析和综合之间的紧张关系中,有一些东西远远超出了这个主题。我觉得这种紧张感是关于思想、人、文化和世界的一个非常重要的想法。我能';I don’我不能准确地表达它的重量。它';这只是一种感觉
只有整体主义,你是一个笨蛋,一只坐着的鸭子。你不知道。有了理性,有了分类、辨别和剖析的能力,你就是危险的武器。你以为你知道,但你还是不知道';t、 有了理性的根基,重新学习整体主义是非常必要的,这样才能超越它,感受这个世界,而不会把你接触到的东西变成灰烬
那里';这是一种刻板印象,一个士大夫在工作期间坚持儒家学说,退休后沉迷于道教。也许是真的,推动过去琐碎的分析会让人更难容忍胡说八道,更难成为丑陋系统中的一个齿轮,更难接受责任和牺牲。但它也会让你在工作中表现出惊人的效率,因为那些你认为有足够价值去做的事情
这是我更高的目标';我把这里设定为一个小小的领域,它本身对生命几乎没有什么可说的。唐';不要被困在模型里。看过去。然后你可以看到,踢就是踢
我们';我一直在谈论变量,把值放入我们用方便的名称访问的框中。所有这些盒子所在的地方叫做内存
记忆没有固有的结构或意义。它是一个非常长的一个字节(八个二进制位,最大值255)框序列,从0开始编号,一直到数十亿。所有这一切都意味着,内存中包含的字节序列是由程序员强加的,因此,他们编写和运行的程序也是由程序员强加的
当一个程序启动时,操作系统会给它一种错觉,以为它是机器上唯一的程序。它的地址空间从特殊的0地址unwritable开始,用作标准的空值,其他任何东西都不会破坏它的保留
它不需要';不要这样:众所周知,msdos将所有内存的完全控制权传递给任何程序,这意味着所有程序都以root运行,而多任务处理没有';不存在。但是unix风格的虚拟内存允许每个程序按照自己认为合适的方式安排自己的内存,同时保护其他程序和操作系统免受错误行为的影响。按照惯例,内存通常有几个只读部分,外加两个主要的可写部分,称为堆栈和堆
堆栈是一个后进先出队列,从内存中的最后一个地址开始,向下扩展,指向较低的地址。每次调用函数时,都会在堆栈顶部添加一个堆栈帧。堆栈框架是一块空间,其中一个函数';s参数和内部存储都是实时的。当一个函数返回时,它的堆栈帧被忽略,新的堆栈顶部再次是最初调用它的函数
堆从0地址之后的某个地方开始,向更高的地址增长。它是持久存储,由程序员直接控制,与函数堆栈中的位置无关。您可以通过函数malloc直接向操作系统请求特定大小的内存块:
在这里,为16个整数分配了足够的空间。每个整数是多个字节,需要存储大于255的数字。我们的记忆块被称为a";缓冲区和#34;malloc返回指向其第一个字节的指针
除非被释放,我们的缓冲区将在程序的整个生命周期中持续存在。当我们';我们告诉操作系统,这样它就可以将空间回收到分配内存的池中:
堆栈和堆以相反的方向(相互)增长的原因是,如果程序需要在其中一个上分配比另一个多得多的内存,那么它们就可以使用所有可用的内存
正如我们';我们已经看到,内存中的每个位置都有一个地址,我们命名的框';我一直在把数据存储在内存中。我们可以取下任何一个盒子,将其存储起始地址作为一个值,然后存储到其他地方:
p类型中的星号表示该变量存储指针,即内存地址。符号AND是运算符的地址,返回变量使用的存储器的地址
然后可以取消对地址的引用,即可以跟随指针,并检索其指向的地址处的值:
函数malloc返回void*类型,即可以指向任何对象的指针。遍历指针以使用或操作存储在其地址的值通常称为";间接";因为这是一种间接的数据访问机制
指针可以嵌套任意深度。可以有指向指针的指针,等等,形成大型抽象图形。唐';t真的";巢穴"从根本上说,指针只是存储在内存中的数字,就像大整数一样。它';这只是因为他们';重新解释为具有任何意义的内存地址
因为c中的字符串只是连续的字符序列,所以字符串集合可以用双指针字符**表示。它';使用双指针来调整容器大小很有用,这样函数就可以在需要时隐式地重新分配存储,而不会中断引用。复杂对象通常由指向结构的结构表示,而结构本身指向结构。等等
与顺序执行不同的是,循环本身并不是以递归的方式实现的。这方面的经典例子是阶乘和斐波那契函数,其中任何结果都取决于相同计算的所有先前结果。这使得它们可以通过递归简洁自然地表达(尽管通常效率很低):
因式阶乘(因式n){if(n==0){return 1;}else{return n*阶乘(n-1);}
在这里,如果调用factorial(5),函数将落入else分支,并尝试计算5*factorial(4)。这需要对函数进行另一次调用,再调用一次,直到达到";基本情况";n=0。然后,一系列的返回有效地解除了迄今为止所进行的所有函数调用,从而产生结果120
巧妙地使用递归可以让你以比简单循环更复杂的方式进行迭代。例如,使用递归比使用循环更容易连续地细分一个组以在子集上执行某些任务。对集合进行排序或在复杂的嵌套层次结构中搜索项的各种方法自然适合以这种方式进行细分或累积
在一种具有一流函数的语言中,事情变得更加有趣。也就是说,可以分配给变量、作为值传递并在运行时构造的函数(关键是,捕获构造它们的环境)。c有前两个属性,但没有第三个。此外,它的函数指针语法是出了名的麻烦,而且这种语言不适合这种编程。javascript的核心是最糟糕的scheme方言,它具有所有必要的特性
在一种具有一流函数的语言中,您可以轻松地构造functi
......