问题

编程到底难在哪里?

回答
编程的难度是一个非常复杂且多维的问题,它并非单一因素造成的,而是由一系列相互关联的挑战共同构成的。下面我将尽量详细地阐述编程的难点,并尝试从不同的角度剖析:



1. 理解抽象概念与逻辑思维的深度要求

编程的核心在于将现实世界的问题转化为计算机可以理解和执行的指令。这个过程需要高度的抽象思维能力。

抽象是编程的基石: 你需要将具体的事物(例如“用户登录”、“购物车”、“数据排序”)抽象成模型、类、函数、算法等。这要求你能从复杂的细节中抽离出核心逻辑,并用计算机能够理解的结构来表示。
例如: 在设计一个社交媒体应用时,你需要抽象出“用户”这个概念,它包含属性(用户名、密码、头像、好友列表)和行为(发布动态、点赞、评论)。如何定义这些属性和行为,以及它们之间的关系,就是抽象的过程。
逻辑思维的严谨性: 计算机是严格按照你设定的逻辑来执行的。任何微小的逻辑错误,如条件判断的疏忽、循环的边界问题、变量作用域的混淆,都可能导致程序无法运行、运行出错甚至产生灾难性的后果。
例如: 在写一个计算平均分的函数时,如果你忘记处理“除数为零”的情况(比如没有学生提交作业),程序可能会崩溃。
因果关系的理解: 你需要理解代码执行的“因果链”。每一行代码的执行都可能对后续的程序状态产生影响。你需要能够预测这种影响,并确保其符合预期。
例如: 当你在调试一个bug时,需要追溯是哪一行代码引入了问题,以及它是如何影响其他变量和程序的流程的。



2. 学习曲线的陡峭性与知识的广度

编程领域的知识体系庞大且不断更新,学习过程中需要不断地吸收新概念和技术。

语言和框架的众多选择: 你不会只学习一种编程语言。不同的语言(Python, Java, C++, JavaScript, Go, Rust等)有不同的语法、特性和设计哲学。同时,为了解决特定问题,你还需要学习各种框架(React, Spring, Django, TensorFlow等)和库。这就像学习不同的外语和文化,每一种都需要时间和精力去掌握。
概念的层层递进: 学习编程并非一蹴而就,很多概念是相互依赖、层层递进的。例如,要理解面向对象编程,你可能需要先理解数据类型、变量、函数;要理解网络编程,你可能需要理解TCP/IP协议、HTTP协议。
不断变化的技术栈: 计算机科学和软件工程领域的技术发展速度非常快。新的语言、框架、工具和方法论层出不穷。这意味着即使你掌握了当前的技术,也需要持续学习以保持竞争力。
例如: 十年前流行的前端框架可能现在已经不再主流,你需要学习新的技术来跟上时代。
底层原理的理解(进阶): 仅仅学会使用工具是不够的,真正深入理解时,你需要了解操作系统原理、计算机网络、数据库原理、编译原理等底层知识。这些知识更具挑战性,需要更深的数学和理论基础。



3. 调试与问题解决的复杂性

绝大多数的编程时间都花在“找bug”和“解决bug”上,这是一种特殊的、需要耐心和系统性思维的问题解决过程。

隐藏的错误: 很多bug并非显而易见,它们可能只在特定的输入、特定的时间或特定的环境下才会出现。这些“隐性bug”往往最难定位。
错误信息的解读: 编译器和运行时环境会提供错误信息(错误提示、堆栈跟踪),但这些信息往往是技术性的,需要具备一定的经验才能准确理解其含义。
例如: 一个“NullPointerException”可能意味着你在尝试访问一个尚未初始化的对象,但具体是哪个对象,需要你根据堆栈信息去逐一排查。
“盲人摸象”的困境: 当你不知道问题的根源时,尝试各种解决方案可能会让你陷入“盲人摸象”的困境,不断地尝试,却找不到问题的关键。
影响范围的评估: 一个看似微小的改动,可能会对程序的其他部分产生意想不到的影响。你需要具备评估这种影响的能力。
缺乏直接反馈: 很多时候,你编写的代码不会立即给出你想要的结果,你需要通过编译、运行、测试来验证你的想法。这个反馈周期有时会很长。



4. 沟通与协作的挑战

现代软件开发很少是单打独斗,团队协作是常态,而协作本身就带来了沟通和理解的难度。

代码的理解与维护: 你需要能够读懂别人写的代码,并能让别人读懂你写的代码。代码的可读性、注释的质量、命名规范等都至关重要。
例如: 你接手了一个没有注释、命名混乱的项目,理解其逻辑会非常困难。
需求的沟通与理解: 软件开发是为了满足用户的需求,而用户的需求往往是模糊的、变化的。你需要与产品经理、设计师、测试人员进行有效的沟通,确保理解一致。
例如: 产品经理说“我们需要一个更快的搜索功能”,这个“更快”到底是多少倍?是前端优化还是后端算法优化?都需要进一步明确。
版本控制与冲突解决: 在团队协作中,你需要使用版本控制系统(如Git)来管理代码。不同开发者修改同一部分代码时,可能会产生冲突,需要协调解决。



5. 解决非技术问题的能力

编程不仅仅是写代码,还涉及到很多非技术层面的问题。

耐心与毅力: 编程过程中会遇到无数挫折,需要极大的耐心去学习、去调试、去解决问题。
细节的关注: 一个错位的括号、一个拼写错误的变量名都可能导致程序失败。编程需要你对细节极其敏感。
学习策略: 如何有效地学习新的技术、如何查找资料(Google、Stack Overflow)、如何利用工具(IDE、调试器),这些都是重要的“学习如何学习”的能力。
时间管理与项目规划: 在大型项目中,如何合理分配时间、规划开发进度、管理风险,也是编程者需要掌握的技能。



总结

编程的难度体现在:

思维层面: 需要强大的抽象能力、严谨的逻辑思维和因果分析能力。
知识层面: 需要掌握多门语言、框架、库,以及底层原理,并且知识体系不断更新。
实践层面: 调试过程复杂且耗时,需要极大的耐心和系统性的问题解决能力。
协作层面: 需要与人有效沟通,理解和维护他人代码,处理协作冲突。
软技能层面: 需要具备极大的耐心、毅力、对细节的关注以及高效的学习策略。

总而言之,编程是一个需要不断学习、不断思考、不断实践的智力活动。它的挑战性在于它要求我们在一个高度抽象且规则严谨的体系中,用一种精确到极致的方式来描述和解决问题。而这种精确性和抽象性,恰恰是它最迷人但也最困难的地方。

网友意见

user avatar

普通人:

我今天要买一斤苹果。



程序员:

我今天要买一斤苹果。

因为我只喜欢红富士苹果,所以我只买红富士苹果。

我能接受的最高价格是10元/斤。

正常情况下一斤苹果用一个袋子能装下,但是为防万一,我会带两个袋子。

我知道附近的3家水果店,所以我会依次访问这3家水果店。

根据上述条件,我设计出以下的买苹果的流程:

       买苹果流程开始     对水果店0、水果店1、水果店2依次执行:     拜访一家水果店流程开始         走到此水果店         如果此水果店没有开门,则结束当前的“拜访一家水果店流程”         如果此水果店没有苹果,则结束当前的“拜访一家水果店流程”         如果此水果店的苹果当中没有红富士苹果,则结束当前的“拜访一家水果店流程”         如果此水果店的红富士苹果剩余不到一斤,则结束当前的“拜访一家水果店流程”         如果此水果店的红富士苹果的价格高于10元/斤,则执行3次:         讲价流程开始             询问店主是否愿意将价格降到10元/斤或更低             如果店主愿意,则跳过剩余的“讲价流程”         讲价流程结束         如果此水果店的红富士苹果的价格仍然高于10元/斤,则结束当前的“拜访一家水果店流程”         打开一个袋子,将其作为当前的袋子         重复执行以下流程,直到总重量大于一斤:         装袋一个苹果流程开始             从所有的不在袋子中的红富士苹果中选出最好的一个             如果此苹果能装入当前的袋子,则将此苹果装入当前的袋子,否则执行:             换袋子流程开始                 如果我有剩余的袋子,则从中任意选出一个并作为当前的袋子,否则执行:                 向店主要袋子流程开始                     向店主索要一个袋子                     如果店主拒绝给我袋子,则将我的所有袋子里的所有苹果取出,然后结束当前的“拜访一家水果店流程”                     将店主给我的袋子作为当前的袋子                 向店主要袋子流程结束             换袋子流程结束             测量我的所有袋子里的所有苹果的总重量         装袋一个苹果流程结束         根据我的所有袋子里的所有苹果的总重量和店主给出的价格,计算我应付的价格         向店主询问我应付的价格         如果我不接受店主索要的价格,则执行3次:         校对流程开始             向店主解释我计算出的价格,并询问其是否同意             如果店主同意,则跳过剩余的“校对流程”         校对流程结束         如果我仍然不接受店主索要的价格,则将我的所有袋子里的所有苹果取出,然后结束当前的“拜访一家水果店流程”         如果我没带钱,则将我的所有袋子里的所有苹果取出,然后结束当前的“拜访一家水果店流程”         付钱拿走苹果         跳过剩余的“拜访一家水果店流程”     拜访一家水果店流程结束 买苹果流程结束     

这个流程怎么样?我来设计一些测试样例,测试一下这个流程。

测试发现一个问题:如果水果店0和水果店1都有红富士苹果并且价格都低于10元/斤,而且水果店1的价格比水果店0更低,那么我希望买水果店1的苹果,但我设计的流程会让我买水果店0的苹果。

为了解决这个问题,我应该先询问所有水果店的价格,然后去价格最低的那一家买苹果。

经过修改,我重新设计出以下的买苹果的流程:

       买苹果流程开始     对水果店0、水果店1、水果店2依次执行:     询问一家水果店的红富士价格流程开始         走到此水果店         如果此水果店没有开门,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程”         如果此水果店没有苹果,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程”         如果此水果店的苹果当中没有红富士苹果,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程”         如果此水果店的红富士苹果剩余不到一斤,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程”         向店主询问此水果店的红富士苹果价格并记录     询问一家水果店的红富士价格流程结束     从3家水果店中选出红富士价格最低的一家(如果有并列则随机选择),将其作为目标水果店     如果目标水果店的红富士苹果价格为无穷大元/斤,则结束当前的“买苹果流程”     走到目标水果店     如果此水果店的红富士苹果的价格高于10元/斤,则执行3次:     讲价流程开始         询问店主是否愿意将价格降到10元/斤或更低         如果店主愿意,则跳过剩余的“讲价流程”     讲价流程结束     如果此水果店的红富士苹果的价格仍然高于10元/斤,则结束当前的“买苹果流程”     打开一个袋子,将其作为当前的袋子     重复执行以下流程,直到总重量大于一斤:     装袋一个苹果流程开始         从所有的不在袋子中的红富士苹果中选出最好的一个         如果此苹果能装入当前的袋子,则将此苹果装入当前的袋子,否则执行:         换袋子流程开始             如果我有剩余的袋子,则从中任意选出一个并作为当前的袋子,否则执行:             向店主要袋子流程开始                 向店主索要一个袋子                 如果店主拒绝给我袋子,则将我的所有袋子里的所有苹果取出,然后结束当前的“买苹果流程”                 将店主给我的袋子作为当前的袋子             向店主要袋子流程结束         换袋子流程结束         测量我的所有袋子里的所有苹果的总重量     装袋一个苹果流程结束     根据我的所有袋子里的所有苹果的总重量和店主给出的价格,计算我应付的价格     向店主询问我应付的价格     如果我不接受店主索要的价格,则执行3次:     校对流程开始         向店主解释我计算出的价格,并询问其是否同意         如果店主同意,则跳过剩余的“校对流程”     校对流程结束     如果我仍然不接受店主索要的价格,则将我的所有袋子里的所有苹果取出,然后结束当前的“买苹果流程”     如果我没带钱,则将我的所有袋子里的所有苹果取出,然后结束当前的“买苹果流程”     付钱拿走苹果 买苹果流程结束     

现在这个流程是不是完美了呢?不是,我还能发现很多问题。
如果3家水果店都有红富士苹果但都不到一斤,但是三家店加起来能达到一斤,那么我不应该结束流程回家,而是应该把三家店的红富士苹果都买下来。
如果我向水果店询问价格的时候这家店还有红富士苹果,但我询问完所有水果店的价格后这家店的红富士苹果卖完了,那么我的流程会让我试图处理不存在的红富士苹果。
我走路的过程中可能会遇到突发事件,比如发现了新的水果店,比如袋子破掉了苹果掉一地,对于这些情况我的流程都无法进行处理。

啊问题太多了我懒得再改流程了。我还是去X宝买吧。
那么接下来我要设计一个在X宝买红富士苹果的流程……

==========================================================

这篇沉寂了5天只得到1个赞的回答竟然一夜之间收获2000+赞,一举成为个人最多赞的回答。感谢大家的支持,以及精彩的评论。

这篇回答并不是讲述在生活中程序员如何买苹果,而是以买苹果为例说明程序员如何解决问题。程序员需要对问题进行透彻的分析,理清其涉及的所有细节,预测可能发生的所有意外与非意外的情况,列出解决方案的所有步骤,以及对解决方案进行尽量全面的测试。

而这些正是我认为编程难的地方。任何一点遗漏都会成为bug,轻则导致挨骂,重则导致经济损失甚至危害安全。

注意这些难点全部和语言无关。和编程所需要的绝对严密的逻辑相比,语言实在是太简单了。(某些自带代码混淆效果的语言除外)

==========================================================

我勒个去4000+赞了!妈妈!我火了!

好多评论说这代码像python。然而我根本不会讲python。

==========================================================

本回答可以随意转载,只需附带原文链接即可。

==========================================================

好不容易写了篇高赞回答,不打个广告是不是有点浪费?

求偶。本人男,爱好女,27(划掉)28岁,Google员工,现居湾区。要求对方同样现居湾区并有稳定身份(不符合者抱歉勿扰)。有意者请私信。(注:已失效

更新(5/30/2021):

这篇4年前的老回答竟然又火了一波,我已经不理解互联网是如何运作的了。

上述广告仍然有效。我是一名会及时更新文档的程序员,如果哪天脱团了会回来更新这条广告的。湾区是指美国加州的湾区;稳定身份是指可以在美国长期生活(例如持有绿卡或者在申请绿卡)。

广告是2020年加上的,所以并不是说我4年一来一直单身,不过现在确实是。

==========================================================

更新(2/15/2022):

上述广告已失效!我脱单了!

加一套免费推广:Coffee Meets Bagel,为寻找serious relationship的人而设计的约会平台。

user avatar

这回答隔了数年被 @微调 小姐姐点赞之后又引来了大量看热闹的观众和评论; (相比之下费劲精力写的专栏技术文章基本没人看真是惨)

想在这里统一回复一个关于认为做“最新东西”写的乱点没问题,反正最后会重构的问题;

这个理论对么?当然对,但是有一个前提,就是做的东西简单, 更确切的讲, 好测, 能很容易验证基本逻辑或者系统的normal path;

对于不好测,且出一个bug就影响重大, 可能会导致黄掉整个项目,那么程序复杂度就不能过于给系统添麻烦,程序复杂度和业务复杂度不成基本线性关系,说明这个程序员写程序没有scalability,做点简单东西还行,做复杂东西就原地爆炸;

程序员产出的code能不能在复杂度这个维度上scale,是我想指出的东西;

————————原文分割线————————


记得之前组里来了一个美国实习生小伙子,很geek的那种,干活快,一天能给你写2000行代码(我code review的速度跟不上他写的速度),让做什么东西,上午告诉做个这个功能,下午就能在测试环境跑起来演示了。跟他单独开会的时候,他说觉的普通的编程没什么意思,太简单了,写程序这方面已经没什么追求了,他比较想跟我研究大数据的框架,数据库,或者机器学习之类的工作,做设计,早日脱离代码这种无脑工作。

我足足花了1周时间,每天review他的代码到凌晨。给他写的comments反馈快赶上我在知乎写的答案文章之和了。。。期间几小时几小时的开会论战,孩子狂,语速快,脑力灵,辩论角度刁钻。他天天要与我论战,看我的评语,速度还算慢下来了。

没来得及讨论完,隔周我要休假了,2周。交代了些他要做的工作。

2周回来,让他改的那个java包爆炸了,本来我们一个支持了7个功能的框架包,总代码量也就5k把,等我回来这包代码量1w5+。也就是说他为了一个小功能加了1w行代码。

这没法review,只能跟他坐一块,先让他给我讲讲这代码都干什么的,然后他说:

(沉思+100)这块我现在也看不太懂当时为什么这么写了。。。

(沉思)这边写的比较复杂是因为当初那边是那样写的,所以这边没办法才只能这么写。

(沉思)把当初那边改好很麻烦,影响也很大,不如就这样吧。

(沉思)这里这么写是因为你看着里是这样的, 然后这里有这个逻辑,然后这里。。。(来回来去翻n个类之后)。。。 所以你看我这里虽然写的比较诡异,但是完全没问题的!(得意ing)

(沉思)这边做的这么奇怪是因为有个bug,通过这么写,这个就bug没了,我也不知道怎么回事。。。所以你看我在这边注释,这行不能删了。。。

(沉思)我觉得这个功能很酷,你们虽然现在不需要,不过有总比没有好吧,将来如果……%¥……&%&……%*7&%……*%…(我没听懂)的话,这个就很有用!!

...

一次一次被我打回去重写,后来总算简化成大概5k行了;临走时候跟我说:你这样编程也太难了。。。

再后来由于一些额外复杂的代码造成我们实现新东西会很复杂,我又重写了一遍,总共大概不到1k行代码。


这里边有几件事情我想说:

  1. 做出来容易, 做正确难,这里做出来指没bug且完成需要的功能,这是最基本要求,不多加讨论。这里正确,不是指功能正确,而是指程序可以很容易推理理解,理解意图, 理解如何做到的,理解为什么系统不会出错。理解为什么要这么做。正确是现在怎么写不会挖坑害将来的人,现在怎么写能让别人1年后看你代码时候不可能理解错你现在的意图,现在怎么写能在别人将来犯错的时候提示他你错了。
  2. 编程是给未来的未知人讲故事,你无法知道将来这个人是谁,他都懂什么,他经历过什么,这个系统将来已经是什么样子了。我们需要在这种无知,缺乏信息的情况下做决定,从千万种把这件事做出来的方法里,选出你觉得最能把这个故事给讲好的那种方式,把故事写下来。编程是一种沟通,沟通是一种艺术,用程序跨越时空之沟通则是一门属于程序员的特有的艺术(就好比数学家用数学公式来沟通) coding is all about the art of communication(引用)。
  3. 坏的决定会导致坏的决定,甚至导致人们去扭曲一个好的决定去迎合坏的决定。垃圾会制造垃圾,一个放在系统里不经清理的额外复杂度,会导致更多的额外复杂度的生成。
  4. 每个人甚至同一个人的不同时刻都有自己的不同的制造额外复杂度的缺陷,比如我每年去看去年自己写的代码,觉得都是垃圾。


然后我又想问几个问题:

我们所在的部门,所在的组,公司,它们的文化,到底是关心作出了一个东西,还是关心做好了一个东西。一个总是给系统添加垃圾,留坑给后人,但是能很快做出能跑起来的系统的程序员,我们到底认为他是做了好事还是做了坏事?我们到底认为他很强,还是他很弱?用超过必要而为了突显技术实力(或者练手)的复杂工具,技术框架搭建系统,做完跑路,在一个组,一个部门,一个公司,那里的文化,到底应该是鼓励还是抑制这种行为?我们又应该如何在一个环境中,去倡导推崇什么样的文化,相遇什么样的人?


人与文化,决定了什么人留在这里,什么人离开,什么人吸引什么人,什么人成长成什么样子。而设计/技术这些枝末细节则必顺应此中的人与文化而自然变化,或自愈,或走向毁灭;哪怕在恶劣的环境中,向下引导,向上规谏,潜移默化,最终改天换日,此为编程之大道也!


下边是定理证明(雾) ( ̄∇ ̄)

最小垃圾存在定律:定义垃圾为系统的总复杂度减去系统的本质复杂度;那么得到:如存在多种方法可以设计与实现一个系统或功能,存在且只存在一种实现会引入最少的垃圾;


垃圾与复杂度正比定律:根据定义可得,系统存在的垃圾越多,系统越复杂;


垃圾倍增定律:基于已有垃圾量a的现状来演化,进化此系统,增加的新垃圾量与已有垃圾量a成正比;


系统腐败定律:当基于垃圾量a来实现新功能的cost大于新功能本身的价值时,系统腐败,需要重构;


战斗人员负战力定律:如果程序员a引入的垃圾,在n次迭代中经过倍增所造成的成本,大于其所清扫的垃圾经过倍增所获得的机会成本,和其实现的新功能价值之和。此时,我们称此程序员战力为负值,其战力绝对值与其引入垃圾的能力和其清扫垃圾的能力的差值成正比


以一敌百存在定律:由负战力定律可知,对所有的自然数n,一个正战力的战斗人员的战力 > (负战力战斗员1+负战力战斗员2+ … 负战力战斗员n)的战力和


系统本质复杂度不可知定律系统表征复杂度无限接近本质定律:取决于战斗人员的知识量,经验,天赋等,对于任何战斗人员n,都必定存在一个战斗人员m(考虑历史长河)使得战斗人员n观察系中的纯净无垃圾系统(复杂度总为1)是战斗人员m观察系中的含垃圾系统(复杂度为1+x),这使得在所有观察系中(包含外星生物),系统的表征复杂度(或者说观察复杂度)无限趋近与本质复杂度。然而我们只能通过观察来感知事物的本质复杂度,却永远无法得知我们离本质复杂度还有多远。(感谢评论区 @尤睿 提出表征复杂度这个概念来纠正此定律)


以有限的生命去追求可以无限的提升的净化方法与视野,我们称之程序艺术家,也就是SDA(Software Development Artist)


… it's extraordinarily important that we in computer science keep fun in computing…

——— Alan J. Perlis (April 1, 1922-February 7, 1990) 《SICP》

打星际… 哦,不, 错了重来… 写程序,你快乐嘛?


写在最后,看到大家最关心的是他拿到正式录取资格了么?还有也许通过我的描述关于他的这个侧面,你会觉得他很不称职。其实不是的,他代码写的绝对是平均值往上的水平,他的问题在于:

  1. 是他根本没有想过去简化业务逻辑,所以很多符合最初需求的代码在简单优化业务逻辑之后完全不需要,
  2. 是自己加了很多功能,
  3. 是自己加了很多自以为是的优化,比如用一个算法估算某个函数的输入数组的最大可能值,然后用那个值来初始化一个数组,因为这样就不会重新分配内存了(他原话)。
  4. 抽象能力有限,这个毕竟经验少, 年轻。
  5. 滥用设计模式(关于设计模式,最多程序员被绊住的一关:设计模式是面向对象编程模型中,应对经典问题的经典解决方案。这里就有两个问题,第一,设计模式的场景用对了么第二,为什么要用面向对象范式,选择编程语言范式时,要从表达力最弱最简单的语言范式开始选择。这叫做最弱表达力原则,而面向对象范式作为最复杂,表达力最强的语言范式,在大多数时候都可以避免使用。关于第二点的论述证明,你可以看concept techniques and models of computer programming这本书。注意,这里说的是语言范式,而不是语言。即使你用java,如果你从来不使用mutable(专业词汇)的功能,和继承。那么你就没有使用面向对象范式)

他其实有非常强的解决问题的能力,想法天马行空,通过自己设计算法来猜函数可能需要的数组大小就可见一斑,还有一个从s3(专业词汇)读数据的需求,他不是简单调api完了,而是写了一个环状buffer(专业词汇),使得网络,硬盘,app可以在理论上最大效率的适应程序当时的场景(为了协调异步,他自己发明了一个很笨拙的promise(专业词汇)),这非常厉害,一般的实习生哪怕sde1可能都写不出来(可惜的是场景会随业务逻辑激烈变化,今天的优化可以是明日的累赘,这就叫做过度优化,过度优化是一种强耦合,会把你的系统死死的钉死在当前版本)。他只是不明白简单是美这件事情而已。如果能有人帮他斧正,日后必成大器。

他最终拿到了正式录取资格,这其中还有个小波折,终审的bar raiser(amazon内部的一个可以一票否决招聘结果的角色)看到他在代码复查系统里跟我的各种激辩,觉得这人不能留。好说歹说才给了正式录取资格。不过最后人家没接,去读博啦。


最最后:在一个相对干净的环境写程序,不断找出新的本来以为不是垃圾的垃圾,对我来说,是一件非常愉快的事情。然而帮别人打扫他本就不该制造的垃圾则是非常痛苦的一件事。

写程序,本应是多么快乐的一件事啊!




====== 小尾巴 =====

用谁都能看懂的方法解释分布式系统

一个书魔程序员的读书简评

编程到底难在哪里?www.zhihu.com

在做程序员的道路上,你掌握了什么概念或技术使你感觉自我提升突飞猛进?www.zhihu.com

请问分布式事务一致性与raft或paxos协议解决的一致性问题是同一回事吗?www.zhihu.com

user avatar

编程难度不在技术上,而是在你想模拟的系统上,也就是难在系统分析。

实践一个简单的算法,或是问题时,会感觉很不错,然而面临复杂的现实世界,你会发现无论通过什么方式去描述,一但只要你想描述,必然就会出现缺陷。

正所谓道可道,非常道。


任何一种思维方式都是对现实的抽象,抽象便是提取特征忽略细节。

人的大脑是有限的,直觉认识并不能完美认识一个复杂系统,所以出现了很多理论工具可以用于修正,然而这些理论工具本身也是抽象的经验集合。

比如说,你这个代码模拟行星运动,将行星看成质点,于是简洁的公式便能描述,而这个是有代价的,每个行星上的石头运动实际上被忽视的。

这种忽视不重要的因素,看起来挺有道理,然而现实是你并不知道其实应该忽视什么。

所以整个系统分析,最难事是难在取舍上,没有银弹说白了就是并不存在完美的模拟现实世界的理论方法。

慢慢体会吧。

user avatar

在不同的行业中,决定人类的工程的复杂度上限的因素是不同的。在制造业中,这个因素主要是成品造价、安全性等因素。而在软件行业中,这个因素是人的脑力极限。所以,编程难,就在于只有软件行业真正触及到了人脑的极限。

看过评论,我还是要强调「只有」。其它行业里,设计人员不是不会触及到智力极限,但是在产品真正投入生产之前,就已经被各种投资方和审核方以成本和安全因素把复杂度降下去了。所以其它行业对智力上限的触及是暂时的。

user avatar

难在心里的恐惧,以及循循善诱的老师,前者尤甚。

首先,如果编程的标准是要成为特别牛逼的程序员,那么,难。但如果只是写代码来提升自己的工作效率,来用机器代替重复工作,那么,一点都不难。

不过很多人都被吓住了,跟以前的我一样。


先说心里的恐惧:

我在掌握了编程思想,并且能够灵活运用之后,跟很多人建议过让他们学编程。无一例外,都被拒绝了,原因是太难了,学不会。

只有一个朋友,有一次想学一个excel作图,让我教他。我说这得用VBA,他一听是编程,马上拒绝了。不过最后在我的反复劝说之下,还是决定尝试一下。整个过程也就半个小时,我就教会他了。

这并不意味着他以后可以自己写程序了,不过类似的问题,他可以通过修改小小的参数和语句,独立完成了。这个朋友之前成绩很差,而且数学更是一窍不通,不过他在整个学习过程中,没有任何问题。

其实只要克服了心理恐惧,编程并没有那么难。这有点像宜家的家具,绝大多数情况下,你不需要自己砍木头,打铁,冶炼,锻造,成型,然后组装。你只需依照宜家的说明书,把运送过来的几个大部件简单的组装好就可以使用了。

绝大多数情况下,你不需要去自己写算法,写最基础的函数,这些东西已经在那里了,你需要的,只是按照说明书用就可以了。比如R,比如python,甚至VBA,里面有大量现存的包,你直接调用,然后一行代码解决所有问题,没有那么恐怖。

另外VBA的录制提供了一个很好的学习方法,它在录制宏的同时,会显示相应的代码。如果你哪个命令不会,直接录制宏,看看系统是怎么显示的就可以,也省去了你上网找的时间。


再说循循善诱的老师:

平心而论,我大学本科的老师也不差,自己还跟清华的老师合作编写教材什么的,业内也也算小有名气。然而他讲课真是不行,四个字概括就是讲不明白。所以我虽然C++及格,但是我仍然什么也不懂,心里也像其他人一样存在着对编程的恐惧。

后来到读研究生,法国的一个编程的老师,只用了6节课,就把所有的思想都讲清楚了。不一定100%准确,不过这对于以后学习其他语言,起到了很大的帮助。

他说简单来看,编程就是无数个函数的组成。任何复杂的函数,其实都是由简单函数组成的。先尝试完成最基本的功能,最小的函数,然后再一点一点把小功能组合起来,形成大功能。

比如画一幅马赛克的图,先学会怎么画一条线,然后画四条,组成正方形,然后染色,组成小的色块,之后再用循环和条件,把这些色块排列起来。

有了这种思想,之后遇到的新语言学习,我基本没遇到什么困难,本质上就是函数,循环,条件。至于每种语言的表述方式,记都不用记,直接网上找文档就可以。虽然现在他讲的java我已经忘干净了。不过他的方法,以及编程的思想,应该是我得到的最大的财富了吧。

user avatar

如果是题主所说的这个事嘛。

难度在于诗篇是给人类看的,人类可以容忍各种各样的不精确描述,就算作者真的没写清楚,也总会有读者通过强行解释给圆回来。

而给计算机看的东西,你必须事无巨细的展现出所有的细节,每个细节全都写得清清楚楚。

所以「做一个登录页面」这种在人类语言中一句话能解决的东西,编程做起来,那也是远远大于这一句话的工作量。

简要的说,程序员要做的事情是把人类语言翻译成计算机可以理解的语言。

编程语言是给计算机看的,也是给人看的,但最终还是给计算机看的。

程序员真正做的事情其实并不是「生成程序」本身,而是「向计算机描述自己想要一个什么样的程序」,然后,真正的程序其实是计算机自己生成的。

虽然程序其实是计算机生成,但「向计算机描述自己想要一个什么样的程序」这件事情比一般人想象的要繁琐得多。

user avatar

我写了大概三四年的代码,觉得编程最难的地方在于:知识和能力的积累并不是一个线性增长的过程。编程能力跟语文和文史类的东西还是有一些不同,并不是说今天背了一篇课文,知识积累5%,明天背了一篇课文,知识累计10%。绝大多数时候,都是今天遇到一个bug,不知道怎么解决,知识积累0%,明天碰到一个很难的算法题,做不出来,知识积累0%。然后经过很多天,似乎就明白了怎么去处理bug。知识积累70%。然后又是某一天,可能悟透了某一类算法,知识积累80%。

所以中间有很多时间会觉得痛苦不堪,感觉自己做出了非常大的努力,但是好像没办法提高。这个感觉就像什么呢,好像金庸笔下的武侠世界,勤勤恳恳,终日习武,却总是在某一个地方卡住。在武学的门口彷徨踱步,却又总是不得门路。但于某日,突然之间,茅塞顿开,任督二脉打通,便一日千里,功力大涨。编程也是这么个事,刚接触编程时,环境不会配置,代码不知如何运行,看着VC6.0的界面,佶屈聱牙,不知道如何是好。若渡了这劫,又过许久,能写几行代码,但是各种error纷至沓来。变量未定义,库函数找不到,重定义,段错误,诸如此般,铺天盖地砸来,心态稍不坚定,便举手投降,直言此生再也不写代码。亦有心志坚定者,过了此关,后续却又遭大难,在动态规划贪心搜索等算法迷雾之中徘徊不得前,于自动微分梯度下降等深度学习混乱之林中茫然不能出,陷于线程锁cache一致访存效率等性能优化之路上恍惚不能进。诸多艰辛困难,于是再也不提计算机理论,专注于业务逻辑。虽也有所成就,但回首往事,总是有些许苦楚,暗自忧愁。编程之难,大抵如此。

至于在此路上攻坚克难。个人愚见,用以抛砖引玉。主要是三个方面,又或者算是三个阶段。第一阶段先看理论,倒不用太过于深入,科普类为佳,没有代码示例的最好。例如计算机原理,操作系统,计算机网络等知识,先用文科的角度切入,多看多读多理解。例如操作系统,分为几块内容,进程管理,内存管理,文件管理。进程管理中,进程是个啥,线程是个啥,进程之间如何交互,交互遇到的问题如何解决。这时不用看具体代码,当作八股文章,记住即可。先大概明白这本书在说一个什么事情,在解决什么具体的问题,建立一个感性的认识。第二阶段,理论联系实践,多写代码。例如,搞清楚了操作系统在干个什么事情之后。就得做点实验,动手写一下,实现一下相关功能,例如实现一下线程的结构,实现一下多线程的交互。或者,难度上升一些,写个简单的操作系统。相关资料见下方repo。

这一步务必困难,十分艰辛,但完成之后将收益巨大,而后也能真正地深入理解线程进程、线程交互等相关概念。再到第三阶段,实践回归理论,撸完了代码,再看看理论知识,这个东西是不是理论说的这个样子,什么会这么实现,后续还会遇到什么问题,应该如何解决。如果觉得还是模糊不清,是不是要继续深入深入。随后再回到第一个阶段,找更深入的书籍,看更新的论文,不断重复上述的三个阶段。

user avatar

谢邀。

关于这个问题,这个人已经回答的很好了:


他在1986年的一次茶话会上发了一篇受邀论文(论文的开头就是“谢邀”),题目就是《没有银弹-软件工程中的根本和次要问题》。 这个人就是IBM大型机之父,Frederick Brooks. 整个论文不太长,有兴趣的同学可以找来读读。在他的经典书《人月神话》中也有收录。 在30多年后的今天,我们在知乎上讨论这个问题,他却早就回答了。他的答案,至今看来,都没有一丝一毫需要修改的地方,毫无瑕疵。

布鲁克斯把软件开发中的困难分为两类: essence,可以译为本质困难或者主要问题,指的是软件开发中不可规避的问题,就是软件本身在概念建构上存先天的困难,也就是如何从问题领域,发展出具体的解决方案。 Accident,可以译为次要因素或次要问题,指的是把解决方案实施到电脑上,所遇到的困难。

他认为软件开发中无法规避的四个特性是: 复杂度; 一致性; 可变性; 不可见性。

他还归纳了在次要问题上我们取得的进步: 高级语言; 分时系统; 统一开发环境。

次要问题我们就不展开了,相信大家都可以理解。 下面所有就都是摘录了,分别描述了4个主要问题。


复杂度。 规模上,软件实体可能比任何由人类创造的其他实体要复杂,因为没有任何两个软件部分是相同的(至少是在语句的级别)。如果有相同的情况,我们会把它们合并成供调用的子函数。在这个方面,软件系统与计算机、建筑或者汽车大不相同,后者往往存在着大量重复的部分。 数字计算机本身就比人类建造的大多数东西复杂。计算机拥有大量的状态,这使得构思、描述和测试都非常困难。软件系统的状态又比计算机系统状态多若干个数量级。 同样,软件实体的扩展也不仅仅是相同元素重复添加,而必须是不同元素实体的添加。大多数情况下,这些元素以非线性递增的方式交互,因此整个软件的复杂度以更大的非线性级数增长。 软件的复杂度是必要属性,不是次要因素。因此,抽掉复杂度的软件实体描述常常也去掉了一些本质属性。数学和物理学在过去三个世纪取得了巨大的进步,数学家和物理学家们建立模型以简化复杂的现象,从模型中抽取出各种特性,并通过试验来验证这些特性。这些方法之所以可行——是因为模型中忽略的复杂度不是被研究现象的必要属性。当复杂度是本质特性时,这些方法就行不通了。 上述软件特有的复杂度问题造成了很多经典的软件产品开发问题。由于复杂度,团队成员之间的沟通非常困难,导致了产品瑕疵、成本超支和进度延迟;由于复杂度,列举和理解所有可能的状态十分困难,影响了产品的可靠性;由于函数的复杂度,函数调用变得困难,导致程序难以使用;由于结构性复杂度,程序难以在不产生副作用的情况下用新函数扩充;由于结构性复杂度,造成很多安全机制状态上的不可见性。 复杂度不仅仅导致技术上的困难,还引发了很多管理上的问题。它使全面理解问题变得困难,从而妨碍了概念上的完整性;它使所有离散出口难以寻找和控制;它引起了大量学习和理解上的负担,使开发慢慢演变成了一场灾难。


一致性。

并不是只有软件工程师才面对复杂问题。物理学家甚至在非常“基础”的级别上,面对异常复杂的事物。不过,物理学家坚信必定存在着某种通用原理,或者在夸克中,或者在统一场论中。爱因斯坦曾不断地重申自然界一定存在着简化的解释,因为上帝不是专横武断或反复无常的。 软件工程师却无法从类似的信念中获得安慰,他必须控制的很多复杂度是随心所欲、毫无规则可言的,来自若干必须遵循的人为惯例和系统。它们随接口的不同而改变,随时间的推移而变化,而且,这些变化不是必需的,仅仅由于它们是不同的人——而非上帝——设计的结果。 某些情况下,因为是开发最新的软件,所以它必须遵循各种接口。另一些情况下,软件的开发目标就是兼容性。在上述的所有情况中,很多复杂性来自保持与其他接口的一致,对软件的任何再设计,都无法简化这些复杂特性。


可变性。

软件实体经常会遭受到持续的变更压力。当然,建筑、汽车、计算机也是如此。不过,工业制造的产品在出厂之后不会经常地发生修改,它们会被后续模型所取代,或者必要更改会被整合到具有相同基本设计的后续产品系列。汽车的更改十分罕见,计算机的现场调整时有发生。然而,它们和软件的现场修改比起来,都要少很多。 其中部分的原因是因为系统中的软件包含了很多功能,而功能是最容易感受变更压力的部分。另外的原因是因为软件可以很容易地进行修改——它是纯粹思维活动的产物,可以无限扩展。日常生活中,建筑有可能发生变化,但众所周知,建筑修改的成本很高,从而打消了那些想提出修改的人的念头。 所有成功的软件都会发生变更。现实工作中,经常发生两种情况。当人们发现软件很有用时,会在原有应用范围的边界,或者在超越边界的情况下使用软件。功能扩展的压力主要来自那些喜欢基本功能,又对软件提出了很多新用法的用户们。 其次,软件一定是在某种计算机硬件平台上开发,成功软件的生命期通常比当初的计算机硬件平台要长。即使不是更换计算机,则有可能是换新型号的磁盘、显示器或者打印机。软件必须与各种新生事物保持一致。 简言之,软件产品扎根于文化的母体中,如各种应用、用户、自然及社会规律、计算机硬件等等。后者持续不断地变化着,这些变化无情地强迫着软件随之变化。


不可见性。

软件是不可见的和无法可视化的。例如,几何抽象是强大的工具。建筑平面图能帮助建筑师和客户一起评估空间布局、进出的运输流量和各个角度的视觉效果。这样,矛盾变得突出,忽略的地方变得明显。同样,机械制图、化学分子模型尽管是抽象模型,但都起了相同的作用。总之,都可以通过几何抽象来捕获物理存在的几何特性。 软件的客观存在不具有空间的形体特征。因此,没有已有的表达方式,就像陆地海洋有地图、硅片有膜片图、计算机有电路图一样。当我们试图用图形来描述软件结构时,我们发现它不仅仅包含一个,而是很多相互关联、重叠在一起的图形。这些图形可能描绘控制流程、数据流、依赖关系、时间序列、名字空间的相互关系等等。它们通常不是有较少层次的扁平结构。实际上,在上述结构上建立概念控制的一种方法是强制将关联分割,直到可以层次化一个或多个图形2。 除去软件结构上的限制和简化方面的进展,软件仍然保持着无法可视化的固有特性,从而剥夺了一些具有强大功能的概念工具的构造思路。这种缺憾不仅限制了个人的设计过程,也严重地阻碍了相互之间的交流。



我的公众号dingshukai666,教编程,欢迎关注~

user avatar

编程不难,或者准确来说,是写代码不难。

对特定领域特定需求给出合理的针对性设计并完美实现,且可以维护,很难。

对于已经探索的比较成熟,需求非常明确,需求重复性高,需求的要求不高的特定领域应用,比如规模不大的网页浏览,现在就已经很简单了,有大量框架可以用。

对于需求复杂,没有直接可以使用的完美吻合需求的框架的,就更难; 如一些比较新的业务方向的。

对于还没怎么被探索过的,需求不十分明确,全新的领域,就十分之难。

真正难的部分在于,你不知道怎么做。

user avatar

程序是解决问题的。现实世界的问题的相当复杂的。编程就是把现实世界的问题抽象成计算机能解释运行的语言的过程。所以编程的难度源于现实世界的复杂性。

类似的话题

  • 回答
    编程的难度是一个非常复杂且多维的问题,它并非单一因素造成的,而是由一系列相互关联的挑战共同构成的。下面我将尽量详细地阐述编程的难点,并尝试从不同的角度剖析: 1. 理解抽象概念与逻辑思维的深度要求编程的核心在于将现实世界的问题转化为计算机可以理解和执行的指令。这个过程需要高度的抽象思维能力。 抽.............
  • 回答
    编制,这个词在中国社会语境下,承载着太多的含义,也引发了无数的讨论。要说它到底有多重要,这个问题其实挺复杂的,因为它重要到什么程度,很大程度上取决于你站在什么位置,以及你对“重要”的定义。但可以肯定的是,有没有编制,在很多方面确实是天壤之别。编制到底是个啥?为什么这么多人趋之若鹜?简单来说,编制就是.............
  • 回答
    袁腾飞在知乎上之所以不受待见,确实是因为他在历史解读上存在一些争议,甚至被不少网友认为存在“瞎话”或者说是带有强烈个人色彩的“歪曲”。要详细说他到底编了哪些,这需要梳理一些普遍被大家诟病的点,并且尽量避免使用过于刻板的AI语式表达,让它听起来更像是一个了解情况的人在娓娓道来。首先,得承认袁腾飞的讲课.............
  • 回答
    小白啊,你这个问题问得可真够到点子上!“厉害的编程代码”,你说得没错,那玩意儿确实不是光写得多就行,也不是谁写的字多谁就厉害。它厉害就厉害在一种“巧”劲儿,一种“精”思妙想,能把复杂的事情给拆解开,然后用一种简洁、清晰、又绝对靠谱的方式给表达出来。你想想,咱们平时跟人说话,有时候说半天,对方还是云里.............
  • 回答
    在高校里,人事代理和正式编制(也常称为事业编制、全额拨款编制、在编人员等)是两种截然不同的用人方式,它们在待遇、稳定性、职业发展、权利义务等方面都存在显著的差异。下面我将尽量详细地阐述两者的区别: 一、 核心区别:用人单位与劳动者之间的关系性质这是最根本的区别。 正式编制人员: 他们是国家事业(.............
  • 回答
    嘿,同为编导生,看到你现在面临的选择,我特别能理解那种纠结和迷茫。高二分班,面对数媒和编导这两个听起来都挺酷炫的方向,确实需要好好权衡一下。你目前的文化成绩在300左右,这个信息很关键,我们得围绕它来聊。先来拆解一下这两个专业,看看它们到底有什么不同,以及它们对你的“文化成绩”这个大前提会有什么影响.............
  • 回答
    socket编程,这事儿说起来,得从网络通信最底层的东西开始聊。你可以把网络想象成一个庞大的邮政系统,而socket,就是这个系统里你用来收发信件的“信箱”或者“电话亭”。它提供了一个统一的接口,让你能在不同的计算机之间,隔着千山万水,互相传递数据。数据是怎么传递的?咱们平常上网,点个网页,发个微信.............
  • 回答
    嗨!准备踏入大学,并且打算自学编程,这真是个了不起的决定!看着眼前琳琅满目的编程语言,挑出第一个“领路人”,确实是件让人有点小纠结的事。别急,咱们就一点点捋清楚,帮你找到最适合你的那条路。别被“最好的语言”晃了眼,问问自己更重要首先,我想跟你说,编程世界里没有绝对的“最好”,只有“最适合”。一上来就.............
  • 回答
    冯大辉(Fenng),作为中国互联网圈子里一位非常知名的人物,他的编程能力一直备受关注,同时也是一个常常被讨论和“神化”的话题。要客观、详细地评价他的编程能力,我们需要从多个维度来审视:一、 起点与早期贡献: 早期开发者背景: 冯大辉的职业生涯始于技术开发。在那个互联网早期,尤其是中国互联网发展.............
  • 回答
    关于“到底存不存在不适合学编程的人”,这真是一个能引起不少争论的话题。我个人觉得,如果非要说“完全不适合”,那可能性微乎其微,但要说“学得顺风顺水、得心应手”是否人人皆可,那答案就得打个问号了。咱们先从大家普遍觉得“不适合”编程的人身上聊聊,看看是哪些特质或者情况容易让人在编程这条路上碰壁。1. 缺.............
  • 回答
    ICPC参赛者:不止是“会写代码”的大学生ICPC,全称国际大学生程序设计竞赛(International Collegiate Programming Contest),是全球公认的最具影响力的大学生计算机程序设计竞赛之一。当提及ICPC参赛者,我们脑海中浮现的往往是那些在赛场上神情专注、指尖飞舞.............
  • 回答
    .......
  • 回答
    Shader的编写,这个看似技术与艺术交织的环节,常常让许多团队陷入一个经典的讨论:这究竟是美术的范畴,还是程序员的职责?答案并非非此即彼,而是一个复杂的、充满协作的动态过程。要深入探讨这个问题,我们得先搞清楚Shader到底是什么。你可以把Shader想象成游戏里物品的“皮肤”和“表情”。它不是游.............
  • 回答
    这个问题真是触动了不少人的心弦,尤其是那些正在艺考路上摸索的同学们和他们的家长。究竟编导最后是会选择这条充满挑战但又充满机遇的校考之路,还是会更倾向于综合类院校呢?这背后牵扯的因素可不少,我们得好好聊聊。首先,咱们得明白,“校考”对于编导类专业来说,很多时候几乎是必经之路。 为什么这么说?因为传媒类.............
  • 回答
    “明粉”这个词,在当下的网络舆论场中,往往带着些许褒贬不一的意味。说“明粉”编造谎言,这本身就是一个比较复杂且容易引发争议的论断。因为“明粉”并非一个统一的群体,他们对明朝历史的解读和认知也千差万别。有些人可能只是出于对明朝一段特定历史时期的喜爱,而有些人则可能将其理想化,甚至进行过度美化。要说“明.............
  • 回答
    您好,看到您在事业编和考公务员之间纠结,这是一个很多人都会遇到的难题,尤其是在职业发展和稳定性的权衡上。我来跟您好好聊聊,尽量从一个过来人的角度,把里面的门道掰开揉碎了说,希望能帮您看得更清楚。首先,我们得明白,事业编和公务员,虽然都属于“体制内”,但它们之间是有本质区别的。 公务员: 简单来说.............
  • 回答
    电视剧《大明风华》中出现“礼部尚书领锦衣卫指挥使”的官职设置,在很多观众,尤其是对明朝历史有所了解的观众看来,确实是一个非常不符合历史事实的设定。这直接反映了编剧团队在历史细节把握上的严重不足,甚至可以说是一种对历史常识的漠视。要详细分析这个问题,我们可以从以下几个层面来探讨:一、 明朝官职体系的基.............
  • 回答
    清朝编修的《明史》,确实是个相当复杂的问题,绝非简单一句“全面抹黑”就能概括的。要理解这一点,咱们得从几个层面来看。首先,得明确《明史》是谁编的。它是清朝官方主导编纂的,这一点很重要。在那个时代,新朝代总得有个理由证明自己取而代之是天经地义的,而旧朝代就难免要承担一部分“不合格”的帽子。《明史》自然.............
  • 回答
    在.NET中编写异步Web API可以带来显著的好处,尤其是在处理高并发、I/O密集型操作以及提升用户体验方面。下面我将详细阐述这些好处: 1. 提升吞吐量和响应能力 (Increased Throughput and Responsiveness)这是异步Web API最核心的好处。 并行处理.............
  • 回答
    二战时期,驱逐舰对敌方战列舰、巡洋舰编队发射鱼雷的成功率,绝不是一个简单的数字就能概括的。它像一场生死豪赌,充满了勇气、牺牲与精密的计算。要理解其中的成功率,我们需要深入剖析当时的战术、技术、以及双方的博弈。“狼群战术”的魅力与风险驱逐舰之所以敢于冒险靠近这些巨舰,很大程度上是因为它们扮演着“狼群”.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有