编程零基础入门,简单易学的编程知识讲解
编程是指编写程序的过程。编写程序就是写代码的过程,可以使用记事本或者word等文本编辑器。当然为了更方便通常会使用专门的集成开发环境,integrated development environment,简称IDE来编写代码。一旦完成编写只需要将代码保存为具有特定后缀的文件即可。程序泛指任何网站、软件、脚本、系统等。
但无论是哪种程序都由两个主要部分组成数据和函数。理解数据和函数的概念对学习编程至关重要。
·数据本质上很简单,它是保存在内存条或硬盘上的电子信息,通过不断排列组合的联合一能够看到视频画面点赞数量等。
·编程的主要任务是编写函数来处理数据。编程的难度取决于数据的大小和处理逻辑的复杂程度。有许多人可能对函数的概念不太了解,觉得它似乎很难。实际上函数非常简单。它是一个接收输入参数执行相应功能并输出结果的过程。可以将函数理解为听到呼唤处理事情并回应结果的方式。这符合人类的逻辑思维,调用函数的动作称为调用,意味着调用函数并让其运行一次。
举个例子,来看一个最简单的函数加法。这个加法函数接收两个输入参数,即第一个数字和第二个数字。它的功能是将这两个数字相加并将结果输出。当调用加法函数时传入参数一和二,函数内部执行加法运算最终输出结果三。
7 行代码 3 分钟:从零开始实现一门编程语言
本文最初发布于 Matt Might 的个人博客。
本文介绍了多种解释器实现。通过修改最后一个解释器,你应该可以快速测试关于编程语言的新想法。如果你希望有一种语法不一样的语言,就可以构建一个解析器,把 s-表达式转储。这样,你就可以干净利落地将语法设计与语义设计分开。
实现一门编程语言是任何程序员都不应该错过的经验;这个过程可以培养你对计算的深刻理解,而且很有趣。
本文直击本质,把整个过程归结为:一个面向函数式(但图灵等价)编程语言的 7 行解释器,而其实现只需要大约 3 分钟。
这个 7 行的解释器展示了许多解释器中都存在的可扩展架构——《计算机程序的结构与解释》中的 eval/apply 设计模式:
本文中总共有三种语言的实现:
- 一个使用 Scheme 耗时 3 分钟实现的 7 行解释器;
- 使用Racket重新实现;
- 一个耗时“一下午”实现的 100 行解释器,实现了顶层绑定形式、显式递归、副作用、高阶函数等功能。如果想要实现一门功能更丰富的语言,那么最后一个解释器是一个不错的起点。
最容易实现的编程语言是一种极简的高阶函数式编程语言,名为λ演算(lambda calculus)。
实际上,λ演算是所有主要的函数式语言的核心——Haskell、Scheme 和 ML——但它也存在于 JavaScript、Python 和 Ruby 中。它甚至隐藏在 Java 中,不知道你是否知道在哪里可以找到它。
阿隆佐·丘奇在 1929 年开发了λ演算。
那时,它还不叫编程语言,因为当时没有计算机;没有什么东西可以“编程”。
它实际上只是一个用于函数推理的数学符号。幸运的是,阿隆佐·丘奇有一个博士生叫艾伦·图灵。
艾伦·图灵定义了图灵机,这成为通用计算机第一个公认的定义。
人们很快发现,λ演算和图灵机是等价的:任何能用λ演算描述的函数都能在图灵机上实现,而任何能在图灵机上实现的函数都能用λ演算描述。
值得注意的是,λ演算中只有三种表达式:变量引用、匿名函数和函数调用。
匿名函数的编写采用“lambda-dot”标记法,如下所示:
复制代码
该函数接受参数v ,返回值e 。如果用 JavaScript 编写,上述代码等价于:
复制代码
函数调用的写法是使两个表达式相邻:
复制代码
JavaScript(或其他任何语言)的写法如下:
复制代码
将参数原样返回的恒等函数写法如下:
复制代码
我们可以将恒等函数应用于恒等函数:
复制代码
(返回当然也是恒等函数。)下面这个程序更有意思一些:
复制代码
你能搞懂它做了什么吗?
乍一看,这门简单的语言似乎缺少递归和迭代,更不用说数值、布尔、条件、数据结构等其他东西。这种语言怎么可能是通用的呢?
λ演算达到图灵等价是通过两个最酷的编程黑科技实现的:Church 编码和 Y 组合子。
关于 Y 组合子,我已经写过一篇文章,关于Church编码,也写过一篇。不过,你不想读这些文章也没事,我只需一个程序就可以说服你,λ演算的功能远超你的预期:
复制代码
这个看上去无害的程序名为 Omega,如果你试图执行它,就发现它不会终止!(看看你能不能找出原因)。
下面是用 R5RS Scheme 耗时 3 分钟实现的一个 7 行λ演算解释器。从技术上讲(下文有解释),它是一个基于环境的指示型解释器。
复制代码
这段代码将从 stdin 读取一个程序,解析它,求值并打印结果。(去掉注释和空行,它只有 7 行)。Scheme 的read函数简化了词法分析和解析——只要你愿意生活在“平衡圆括号”(即s-表达式)的语法世界中。(如果不愿意,你就必须仔细研究解析中的词法分析;可以从我的一篇关于词法分析的文章入手)。在 Scheme 中,read从 stdin 中获取括号括起来的输入,并将其解析为一棵树。
eval 和apply 两个函数构成了解释器的核心。尽管是在 Scheme 中,但我们可以给予这些函数概念上的“签名”:
复制代码
eval函数接收一个表达式和一个环境然后转换为一个值。表达式可以是一个变量,一个 lambda 项或一个应用程序。环境是一个从变量到值的映射,用来定义一个开项的自由变量。(开项是一个变量的非绑定出现。)例如,考虑一下表达式(λ x . z)。这个项是开放的,因为我们不知道z是什么。
由于用的是 R5RS Scheme,我们可以使用关联列表来定义环境。
闭包是一个函数的编码,它将一个(可能是开放的)lambda 表达式与一个环境配对,以定义其自由变量。换句话说,一个闭包封闭了一个开项。
Racket是 Scheme 的一种方言,它功能齐备,可以把事情做好。Racket 提供了一个可以清理解释器的匹配结构,如下所示:
复制代码
这个代码多点,但更简洁,更容易理解。
λ演算是一门很小的语言。即便如此,其解释器的 eval/apply 设计也可以扩展到更大的语言。例如,用大约 100 行代码,我们可以为一个相当大的 Scheme 子集实现一个解释器。
考虑一种具有各种表达形式的语言:
- 变量引用,如:x、foo、save-file。
- 数值和布尔常量,如:300、3.14、#f。
- 基本操作,如:+、–、<=。
- 条件:(if condition if-true if-false)。
- 变量绑定:(let ((var value) …) body-expr)。
- 递归绑定:(letrec ((var value) …) body-expr)。
- 变量可变:(set! var value)。
- 定序:(begin do-this then-this)。现在,为这门语言添加 3 个顶层形式:
- 函数定义:(define (proc-name var …) expr)。
- 全局定义:(define var expr)。
- 顶层表达式:expr。下面是完整的解释器,其中包括测试工具和测试用例:
复制代码
下载源代码,请点击https://matt.might.net/articles/implementing-a-programming-language/minilang.rkt?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTU0NTMzMzAsImZpbGVHVUlEIjoibG9xZVcyRXl2d0hkSkxBbiIsImlhdCI6MTY1NTQ1MzAzMCwidXNlcklkIjoyMDQxOTA5MH0.Nv5UyUdCUJNT7c0kIaPSE0g0f4k9Ed26rLl2Bu5RpG4
通过修改最后一个解释器,你应该可以快速测试关于编程语言的新想法。
如果你希望有一种语法不一样的语言,就可以构建一个解析器,把 s-表达式转储。这样,你就可以干净利落地将语法设计与语义设计分开。
查看英文原文:
https://matt.might.net/articles/implementing-a-programming-language?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTU0NTMzMzAsImZpbGVHVUlEIjoibG9xZVcyRXl2d0hkSkxBbiIsImlhdCI6MTY1NTQ1MzAzMCwidXNlcklkIjoyMDQxOTA5MH0.Nv5UyUdCUJNT7c0kIaPSE0g0f4k9Ed26rLl2Bu5RpG4
为什么只有AI编程成功落地?
本文来自微信公众号:阿茶的AI之路,作者:起名贼费劲的阿茶,题图来自:AI生成
原本计划写一篇2024年AI领域的年度总结,但鸽了。现在决定将内容拆分成系列文章。开局先放王炸,聊聊为什么大模型落地喊得火热,但是实际落地的场景只有AI编程。
AI编程无疑是当下大模型落地最成功的一个领域。从Github的Copilot,到Cursor,再到第一个AI程序员Devin。好多人都在说:AI编程找到了PMF(Product Market Fit,产品市场契合)。
但为什么是它?
有人说“因为是真实需求”。难道AI在其他领域就是伪需求吗?
有人说“因为代码比自然语言更容易生成”。真的是这样吗?
还有人说“其他领域的模型能力还不够”。但为什么编程够呢?
这些解释都过于表面,今天就从我的角度来解析为什么AI编程能成功落地,以及它未来的发展。
先从一个问题开始。
一、代码和自然语言,到底哪个更难生成
“代码的关键词少,规则固定,所以更容易生成。”这是解释AI编程为什么好用的常见说法。
听起来挺有道理的?代码就那些关键词,模型只要从有限的词里面挑就行了,采样空间相比自然语言小太多了。
但是什么时候“词少=容易”了?如果真的是这样的话,数学问题的描述足够精简,符号也少。那大模型做数学问题应该更强吧。
显然不是这样。
大模型到现在连JSON都弄不明白。JSON是一种编程领域常用的数据交互格式,在面对较为复杂的JSON时,大模型经常会出现括号对不上、层级关系错乱的问题。
这个“代码更容易生成”的论点,其实混淆了“生成”和“应用”两个阶段。
在自然语言生成中,我们对大模型的容忍度很高。它可以犯语法错误,可以前后矛盾,可以逻辑混乱,我们依然能从中提取有价值的信息。容错性非常高。
但代码生成完全是另一个维度的挑战。就像做数学题,代码能跑就是能跑,跑不通就是报错。它不存在“基本正确“或“大致可用“的中间状态。每一个分号、每一处缩进、每一个变量名都必须精确无误。所以代码生成其实是更难的,因为对代码的可用性要求是远高于文本的。
二、核心:可信验证
代码生成难度更高,为什么它应用得最好呢?那些难度低的领域为什么反而应用效果差呢?真正原因其实是编程具有一种可信验证机制。
所谓可信验证,简单地说,就是一种能够快速、客观地判断AI输出结果的可用性的验证模式。
1. 客观性:验证结果不依赖人或者AI模型的主观判断;
2. 即时性:能够立刻得到验证结果;
3. 确定性:对就是对,错就是错。
接下来我将论述可信验证是怎样让AI编程成功的。
1. 应用端的应用:快速而准确的验证
为什么说编程领域有着完美的可信验证?这让我想到网上流传的一句话:
恋人会背叛你,朋友会欺骗你,但数学不会,因为数学不会就是不会。
答案就藏在代码的本质特性中:程序设计就像数学一样,是一个非黑即白的世界——能跑就是能跑,跑不了就是跑不了。 这种确定性来自一个关键角色:编译器。它负责将代码编译成可执行文件,这个过程是严格符合语法规定的。
在这个过程中,编译器扮演着一个独特的角色:它是第三方的、非AI的、完全可靠的验证机制。它不会被情绪影响,也不会担心被人类诱导,不会有主观偏见,只会忠实地执行语法规则。符合规则就可以编译,不符合就是报错。
这种严格的验证机制成就了AI编程的应用。在AI尝试落地的所有领域中,几乎没有哪个领域能像编程这样拥有如此客观、即时、确定的验证标准。这种验证机制对使用者的要求极低——不需要你懂编程原理,不需要你精通算法,只要能运行代码,就能知道大模型输出的结果是否可用。
为什么要强调非AI?
因为大模型是基于概率的,所以要使用可靠的传统的规则算法。当然,你用更高的模型来验证低模型输出也可以,但这依然是不可靠的。这点会在下一小节继续论述。
注1:为了行文流畅,我忽略了一些细节,例如我把编译和解释同时称作了“编译”……但是这并不是重点。
注2:程序员直接看代码生成质量也算一种可信验证,但这依赖于用户的知识水平。这里只讨论最基础的可信验证机制。
2. 模型端的应用:进击的合成数据
光有可靠的验证机制还不够,模型本身的能力也很关键。(你总不能接受一个只有5%成功率的大模型吧)但有趣的是,大模型在代码领域的进步似乎特别快,而且一直在进步。
这真的只是巧合吗?
业界一直在强调自己家新模型在数学和代码方面的突破,却很少有人说“AI说话更像人了”。为什么?
答案可能会出乎意料:因为训练数据枯竭,大模型目前可能只能在代码这个领域进步。
已经无数人提到过这个问题了,模型的自然训练数据面临枯竭。在大模型训练中,数据和模型架构是同等重要的。数据的枯竭意味着模型能力提升会放缓。目前大模型厂商常用的应对策略:
(1)人工生产新的数据,包括但不限于在网上爬取,或者找人手动编写新的数据;
(2)使用更高级的或者旧的模型合成数据训练新模型。
人工生产新数据的成本高昂,大部分都会采用合成数据来训练。而使用模型生成的合成数据又可能导致模型崩溃。已有大量研究证实,质量差的合成数据和人类语言的偏差会导致后续训练模型的输出越来越偏离人类表达。
那么模型训练方又是如何控制合成数据生成质量的?目前并没有客观的评价标准。主流方案是用更强大的模型来筛选,以及人工主观判断。这不仅成本高昂,还难以规模化,也不够可靠。
然而可信验证机制有效保证了代码合成数据的下限,它缩小了合成数据和人类数据的差异。
代码的验证标准是二元的(能跑/不能跑),能运行并得到正确结果的就是好程序,报错的就是错误程序。这种客观标准让我们可以大规模生成并验证合成数据,效果等价于成千上万个初级程序员在不知疲倦地编写代码,从中挑选可用的代码。
这就是代码合成可靠的根本原因:即使生成的代码质量不高,但只要能通过编译和运行,就具备基本的训练价值。这种低成本的质量保证机制,确保了模型在代码领域能持续进步。其实,大模型生成的代码其实要比很多github上代码质量更高。
3. 可信验证的双重价值
通过上面的分析,我们可以看到,可信验证在AI编程领域发挥着双重作用:
-
在应用端,它让AI编程获得了用户的信任。不需要专业知识,不需要复杂判断,能跑就是能跑,不能跑就是不能跑。这种简单直接的验证机制大大降低了使用门槛,加速了AI编程的普及。而且让很多“零知识用户”也可以进行尝试。
零知识用户:不会编程但想做app的人,这个概念可以引申到其他领域。他们对可信验证的要求极高,因为他们自己不会处理异常情况。
-
在模型端,它解决了AI发展的数据瓶颈。当其他领域还在为训练数据发愁时,编程领域已经找到了可持续的数据来源。可信验证确保了合成数据的基本质量,让模型能力持续提升。
可信验证不仅解决了“用户敢不敢用“的问题,还解决了“模型怎么进步”的问题。在大模型产品toB端,可靠性一直是最大的痛点。但可信验证机制提供了一个极为有效的解决方案 —— 它让输出结果可控、可及时验证,配合原有的代码审查集成机制,大大降低了应用风险。
在可信验证的加持下,AI编程形成了一个良性循环,走出了一条可持续发展的道路。
三、关于AI编程的其他观察
1. AI编程目前的局限性
(1)代码生成质量依然有待提高
虽然有可信验证机制,但目前AI生成的代码质量仍然参差不齐。好在我们可以通过代码覆盖率、复杂度等客观指标来评估代码质量(没错,更高级的可信验证),这些指标又可以反过来指导训练数据的筛选,形成质量提升的闭环。
(2)AI编程对语言支持度不均衡
AI在Python上表现出色,而在Java等语言上相对逊色。这里有两点原因。
首先是训练数据的差异。Python的开源社区活跃,这为大模型提供了海量的高质量训练数据。
其次是语言特性的影响。Python的语法相对灵活,容错性更高 ,这使得AI更容易生成可用的代码。相比之下,Java等强类型语言的语法约束更严格,对代码生成的要求也更高。
2. 自动化会带来额外心智负担
可信验证的即时性还挺重要的,否则会给用户带来意想不到的心智负担。这一点在Devin身上体现得特别明显。
Devin被誉为全球首个AI程序员,号称具备全栈开发、自学新技术、构建部署应用、自主调试等多项能力。
初次体验Devin时,它确实让人感觉非常爽。只要你把任务安排给它,然后就不需要管它了。就像真的拥有了一个实习生可以独立完成任务,让我能专注于其他工作。等着验收就行。
但相比Cursor,Devin存在两个致命问题:
(1)得到反馈的时间要更长,这意味着如果我给他的命令是错的,或者他思维错了,过很久我才会知道。这会严重降低工作效率,沉没成本也更高了。
(2)调试成本剧增。AI生成的代码量越大,debug的难度就越高。因为这些代码不是你写的,你需要额外的时间来理解它的逻辑。而且还有更严重的事情,在你debug的时候,经常会不知道到底是它代码生成的有问题,还是你操作有问题。这点对于零知识用户更为致命。
考虑到AI同样可以debug。我专门做了个实验:完全以零知识用户的身份,让Devin写代码,再用Claude来debug。Devin写了20多分钟的代码,Claude debug了一个小时,功能依然没能跑通。
与自动驾驶不同,开车时你可以随时接管,因为车辆的当前状态是显而易见的。但在编程中,如果AI走错了方向,之前的工作就全部作废了。那几十分钟的等待,就真的变成了纯粹的时间浪费。得到的是你和AI都不想用的一大堆代码,没有任何价值的代码。
注:Devin不好用还有个很大的原因我觉得是背后的自研模型不够强。我用Cursor的Agent搭配Claude,生成的代码质量就高很多。
3. AI编程的未来发展:更高级的可信验证
目前应用端的可信验证还很初级,主要是看代码“能不能跑”,考虑的是终端输出结果。但随着技术发展,会出现更高级的可信验证方法,考虑更多的因素。例如上文的覆盖率这些指标。
现代IDE已经能够自动检测性能隐患和安全漏洞。这些自动化的质量评估机制,本质上也是一种可信验证——它们同样具备客观性和即时性,只是验证维度更加丰富。
其次是自动化测试的进化。即使代码能够运行,也需要验证其功能完整性。自动化测试框架能够生成测试用例、检查边界条件、验证业务逻辑,包括对代码性能进行检测,提供了另一层次的可信验证。这些客观的质量指标同样可以反馈到训练环节。这些进步意味着AI编程可以从“基本可用”进化到“高质量”,Devin这样的产品也会更好用。我依然相信Devin是AI编程的未来,因为这种把人解放的自动化才是真正的自动化。
但是这种AI编程不适合零知识用户,它的未来或许就是极大的增加程序员的生产力。对于零知识用户,或许Dify这样的平台更可靠。
4. 对其他领域的启示
通过分析AI编程的成功,我们其实可以得到一个重要启示:任何想要成功应用AI的领域,都需要找到自己的“可信验证”机制。
不是所有领域都能像编程那样有编译器这种完美的验证工具。也可以借鉴这种思路,在各自领域内建立相对可靠的验证机制。这个验证机制即使早期不能做到100%准确,但至少要能给出一个基本的可用性判断。“要知道模型的下限在哪”。 可信验证不仅能降低使用门槛,还能为模型训练提供可靠的数据来源。
本文来自微信公众号:阿茶的AI之路,作者:起名贼费劲的阿茶
本内容为作者独立观点,不代表虎嗅立场。未经允许不得转载,授权事宜请联系 hezuo@huxiu.com
正在改变与想要改变世界的人,都在 虎嗅APP
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。